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. 引入库:

from pwn import *
  • 经典pwntools,pwn手都熟悉不过,正好笔者在打西湖这段时候在学pwn,所以这里不多介绍

2. 定义函数 get_io

def get_io():    
io = remote("139.155.126.78", 15419)
return io
  • 连接靶机

3. 定义全局变量 positions

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="")

解释:

  1. 根据 positions 提取密文中的位:

    for pos in positions:    
    message += cipher[pos]
    • 遍历 positions 列表,从 cipher 中提取对应位置的字符,并拼接成一个新的二进制字符串 message
  2. 将二进制字符串转换为可读文本:

    for z in range(0, len(message), 8):   
    print(chr(int(message[z:z + 8], 2)), end="")
    • message 按每 8 位分割,并将每一段转换为十进制数,再通过 chr 转换为对应的 ASCII 字符。
    • 最终将解密的结果输出为明文。

队友当时猜到了每个字节对应变化bit,可惜我们都没办法把这个爆破脚本写出来,比较可惜,再接再厉