0x01 前言: 遇见的第一题没有附件的RE题,原来这种题目叫黑盒测试?
0x02 分析 题目只给了一个靶机,连接上去之后提示输入:(没存图片,这里借队友的用一下) 输入不同长度的字符串会提示Wrong length, 总之先把长度给爆破出来,这里可以使用python的pwn库,也就是pwn常用工具pwntools:
//字符长度爆破脚本 from pwn import *context(log_level = 'debug' , arch = 'amd64' , os = 'linux' ) xihu='139.155.126.78' stop=1 text_len='' while (stop): p = remote(xihu,24847 ) text_len+='a' p.sendlineafter('Welcome to dance: ' ,text_len) if b'Wrong' in p.recvline(): stop=1 else : print ('ok' ) stop=0
稍等一下就有结果了,正确的字符串长度为96个字符,这里显示0x61(97)是因为字符串结尾有一个换行符'\n
接下来正常用nc连接然后随便输入96个字符,会得到一串回显: 主要还是留意最后一段Checking with
000011000110000101001000001110100000000101000100110100101000110111001010000010111011100000000010101100011100010110001000011001000110011101101001001001100110100100010001110001110100011100011111110111001011011100001110000110100101000011000011010101110000110101110101101011110000000101011000000101001010001000001010111111111100100010001100000001011101100101000000111101101010001100010110001000010101101100111101111111110101100111000010111110110101010010000011100000011011010010001110011010100101000000011100000010001010010110111000111001000100110101000001101000110001001000000000111100001100100000001010100101101010101001111001010010001111000101011011001001001101000010011101011101011011011110101000110110101000011001000100001101110000101110110000010001000010010001001100
这一串应该是密文
当时比赛就卡在这里了,主要还是一直找不到加密的规律最后就失败了,当时明明字符长度是爆破出来,到这里密文应该要尝试爆破一下才对,唉
赛后看了一下别的师傅的wp,都是爆破,这里拿一下他们的脚本,来详细解析一下做了什么:
from pwn import *def get_io (): io = remote("139.155.126.78" , 15419 ) return io positions = [] def run (): for x in range (96 ): for y in range (7 , -1 , -1 ): payload = bytearray (b'\xff' * 96 ) if payload[x] & (1 << y): payload[x] ^= (1 << y) io = get_io() io.sendline(payload) io.recvuntil("Start from 0" ) result = io.recvuntil("with" ).replace(b"Checking with" , b"" ).strip().split(b"\n" )[-1 ][30 :-4 ].decode() result = "2" + result try : result = result.index("0" ) positions.append(result) except ValueError: positions.append(0 ) run() cipher = "000011000110000101001000001110100000000101000100110100101000110111001010000010111011100000000010101100011100010110001000011001000110011101101001001001100110100100010001110001110100011100011111110111001011011100001110000110100101000011000011010101110000110101110101101011110000000101011000000101001010001000001010111111111100100010001100000001011101100101000000111101101010001100010110001000010101101100111101111111110101100111000010111110110101010010000011100000011011010010001110011010100101000000011100000010001010010110111000111001000100110101000001101000110001001000000000111100001100100000001010100101101010101001111001010010001111000101011011001001001101000010011101011101011011011110101000110110101000011001000100001101110000101110110000010001000010010001001100" message = "" for pos in positions: message += cipher[pos] for z in range (0 , len (message), 8 ): print (chr (int (message[z:z + 8 ], 2 )), end="" )
0x03 脚本解析: 1. 引入库:
经典pwntools
,pwn手都熟悉不过,正好笔者在打西湖这段时候在学pwn,所以这里不多介绍
2. 定义函数 get_io
: def get_io (): io = remote("139.155.126.78" , 15419 ) return io
3. 定义全局变量 positions
:
4. 核心函数 run
: def run (): for x in range (96 ): for y in range (7 , -1 , -1 ): payload = bytearray (b'\xff' * 96 ) if payload[x] & (1 << y): payload[x] ^= (1 << y) io = get_io() io.sendline(payload) io.recvuntil("Start from 0" ) result = io.recvuntil("with" ).replace(b"Checking with" , b"" ).strip().split(b"\n" )[-1 ][30 :-4 ].decode() result = "2" + result try : result = result.index("0" ) positions.append(result) except ValueError: positions.append(0 )
解释 1. 两层循环:
第一层循环 for x in range(96)
:遍历 payload
的 96 个字节。
第二层循环 for y in range(7, -1, -1)
:遍历每个字节的 8 位(bit)。
2. 生成初始的 payload
: payload = bytearray (b'\xff' * 96 )
payload
是一个长度为 96 的字节数组,初始值为全 0xff
(即每个字节全为 1 的二进制:11111111
)。
3. 修改 payload
的某一位: if payload[x] & (1 << y): payload[x] ^= (1 << y)
通过按位操作,清除 payload[x]
的第 y
位(将其置为 0)。
4. 发送 payload
: io = get_io() io.sendline(payload)
通过 get_io
函数连接到远程服务并发送构造的 payload
。
5. 接收服务器返回的响应: io.recvuntil("Start from 0" ) result = io.recvuntil("with" ).replace(b"Checking with" , b"" ).strip().split(b"\n" )[-1 ][30 :-4 ].decode()
读取服务器返回的数据,并提取出与攻击逻辑相关的部分。
6. 分析返回的结果: result = "2" + result try : result = result.index("0" ) positions.append(result) except ValueError: positions.append(0 )
将服务器返回的结果拼接为一个字符串,并尝试找到第一个 “0” 的位置。
如果找到 “0”,将它的索引追加到 positions
列表中。
如果没有 “0”(ValueError
),将索引位置记为 0。
5. 定义密文 cipher
: cipher = "000011000110000101001000001110100000000101000100..."
6. 解密过程: message = "" for pos in positions: message += cipher[pos] for z in range (0 , len (message), 8 ): print (chr (int (message[z:z + 8 ], 2 )), end="" )
解释:
根据 positions
提取密文中的位:
for pos in positions: message += cipher[pos]
遍历 positions
列表,从 cipher
中提取对应位置的字符,并拼接成一个新的二进制字符串 message
。
将二进制字符串转换为可读文本:
for z in range (0 , len (message), 8 ): print (chr (int (message[z:z + 8 ], 2 )), end="" )
将 message
按每 8 位分割,并将每一段转换为十进制数,再通过 chr
转换为对应的 ASCII 字符。
最终将解密的结果输出为明文。
队友当时猜到了每个字节对应变化bit,可惜我们都没办法把这个爆破脚本写出来,比较可惜,再接再厉