三道Pwn,比赛时均没有写出利用,看出并修上两个题的洞,另外一个没看出洞,当然也没修上。
最终排名:
攻防详情:
异或不在的星期天
- 附件: 异或不在的星期天.zip
- 漏洞: 栈溢出
很明显的main函数栈溢出,但是encode函数太大,无法F5,也就看不懂了(后经同学提醒是个魔改的AES),所以也就不知道如何走到return进而触发栈溢出:
虽然不会打,但是咱还是会修的,修的方式比较暴力,直接干掉了return,patch成exit(0):
- 用exit直接退出的原因是:main函数如果正常return则会返回到libc_start_main,进而执行一些收尾工作,比如fini_array啥的,如果这里面没有开发逻辑,直接退出无伤大雅。
- 清空rdi的原因是:不知道怎么check,原来的程序是return 0返回,故保证程序返回和原来一致,防止checker可能不过。
比赛时用keypatch修的,当然也可以用pwntools来生成相应的机器码,首先是清rdi寄存器:
➜ python
>>> from pwn import *
>>> context(arch='amd64')
>>> asm('xor rdi,rdi').encode('hex')
'4831ff'
然后是call指令,这句没法用pwntools直接生成,因为其不支持相对地址的汇编指令生成,本质是因为pwntools是调用了相应架构的as去编译一段汇编,然后提取,并不是按照机器码的生成规则算出一段机器码。不过我们可以直接算出来这个指令,再算之前首先要知道call指令的格式:
所以首先我们要算出这个相对地址,xor rdi,rdi
的起始地址是0x676f4d
,长度是3个字节,call 一个相对地址的机器码长度是5个字节,所以call指令执行时rip的值为0x676f4d+3+5
,找到exit的函数地址如下:
做差:
➜ python
>>> hex(0x676f4d+3+5-0x400450)
'0x276b05'
然后需要转换成补码,即负数在计算机中的存储格式:python 负数 和 任意位数 补码 互转
➜ fu2bu -0x276b05 32
input : -2583301
hex : 0xffd894fb
bin : 11111111110110001001010011111011
------------------------------------------------
bit : 32
min signed : 0x80000000, -2147483648
max signed : 0x7fffffff, 2147483647
max unsigned : 0xffffffff, 4294967295
大小端转换后,开头接上call的操作码E8
,即为call exit
的机器码:
E8 FB 94 D8 FF
指令的机器码可以参考以下两张cheatsheet:
pwntools可以反编译这句:
➜ python
>>> from pwn import *
>>> context(arch='amd64')
>>> disasm("\xE8\xFB\x94\xD8\xFF")
' 0: e8 fb 94 d8 ff call 0xffffffffffd89500'
如果你就想用pwntools来生成跳转语句,也可以。方法是:使用寄存器间接保存目标地址,比如call rax
这种,不过注意这里使用的就是绝对地址了:
➜ python
>>> from pwn import *
>>> context(arch='amd64')
>>> asm('mov rax,0x400450;call rax').encode('hex')
'48c7c050044000ffd0'
baby_httpd
- 附件: baby_httpd.zip
- 漏洞: 格串、堆溢出
本题是一个将python打包的ELF程序,所以需要反解成原始的python代码,否则看不出程序逻辑:
首先安装需要打包与反编译python字节码的库:
➜ sudo pip install pyinstaller
➜ sudo pip install uncompyle6
解开ELF中的python代码:
➜ objcopy --dump-section pydata=pydata.dump pwn
➜ python pyinstxtractor.py pydata.dump
找到input_httpd文件,按上文补齐文件的头部magic并增加.pyc
后缀,最后使用uncompyle6
反编译即可导出原始python文件:
➜ uncompyle6 input_httpd.pyc
找到一个溢出和一个格串,patch如下,溢出没有完全修上,但是官方的exp应该是失效了:
➜ diff ./http.py ./patch/http.py
199,200c199,200
< string = ctypes.c_buffer(1024)
< self.libc.sprintf(string,"%s",value)
---
> string = ctypes.c_buffer(2014)
> self.libc.strcpy(string,value)
patch完之后,重打包回去即可:
➜ pyinstaller -F ./http.py
dwarf
- 附件: dwarf.zip