Brrr bing bing bing bing da da da.

nc okboomer.tasteless.eu 10601

Update: We were asked about the environment this challenge is running on. It does not affect the solvability, but here is this information nonetheless: Linux 5.4.0-1025-aws / Ubuntu 20.04.1 LTS

chall.py

425 Pts, 14 solves, pwn, shellcode

I quite like doing shellcoding challenges, so was pretty excited to see one in Tasteless CTF as it’s been a while since I’ve done one.

After downloading the python script and having a look you can see that it reads in our assembly code, validates that each line matches the regex ^mov\s[\w\[\],\s]*;?$, then adds it to a template. It’s then compiled and run twice, the first time the output needs to be TASTE and the second time the output needs to be LESS!.

The template used is the following:

.intel_syntax noprefix

.text
.globl  _start

_start:
    xor rcx, rcx
    mov rdx, 0x1000
    mov rsp, 0x7ffffffff000
u_no_env:
    push rax
    dec rdx
    cmp rdx, 0x00
    jnz u_no_env
    mov rsp, 0x7ffffffff000

    {}
    mov rax, 1
    syscall

    mov rdi, 0x42
    mov rax, 0x3c
    syscall

This first nulls out 0x1000 bytes of the stack, runs our code, then does a write and an exit syscall.

Looking again at the regex again ^mov\s[\w\[\],\s]*;?$ it doesn’t allow for all mov variants, as you cannot use *+- which rules out things like mov rax, [rbx + 0x100].

I was originally thinking about using something like mov rax, 0x90909090909090 and getting rip to jump into the middle of the instruction, allowing ops other than mov to be used. The issue is that the template ends with an exit syscall and the is no ret so we can’t put an address on the stack to redirect the control flow, the stack is non-executable, and the text section is not writable.

The next thing was to try and find some randomness, if something was different in between runs then it could potentially be used. The stack was nulled out which would be the goto place, but looking in gdb there are a few other places:

gef  vmmap
[ Legend:  Code | Heap | Stack ]
Start              End                Offset             Perm Path
0x0000000000400000 0x0000000000401000 0x0000000000000000 r-- /vagrant/tasteless20/movcode/tmp/a.out
0x0000000000401000 0x0000000000402000 0x0000000000001000 r-x /vagrant/tasteless20/movcode/tmp/a.out
0x00007ffff7ffb000 0x00007ffff7ffe000 0x0000000000000000 r-- [vvar]
0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 r-x [vdso]
0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack]
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 --x [vsyscall]

I tried dumping the vdso and vsyscall between runs, but the files were identical. For some reason gdb was unable to access the vvar section so I wrote a quick something to print it out based on the challenge template:

.intel_syntax noprefix

.text
.globl  _start

_start:
    xor rcx, rcx
    mov r12, 0x3000
    mov rdx, 1
    mov rdi, 1
    mov rsi, 0x00007ffff7ffb000
u_no_env:
    mov rax, 1
    syscall
    dec r12
    inc rsi
    cmp r12, 0x00
    jnz u_no_env

    mov rdi, 0x42
    mov rax, 0x3c
    syscall

Compiling it and running the same way as the challenge (it disables aslr with setarch) showed that there was some differences between each run:

With this knowledge we can use an instruction such as mov al, byte ptr [0x00007ffff7ffb08b] to load a byte into rax that will be different each run.

This random byte can be used as an index to a lookup table with an instruction like mov dl, byte ptr 0x401028[rax], if every even byte was one value and odd byte was another then there would be a 50/50 chance of either being used.

To create the lookup table I used 128 mov al, 0xba instructions, as in hex it converts to 0xb0ba. This allows us to load something at offset 0xb0 one time and 0xba the other. If we create two mov instructions and encode an address into the second argument, we can randomly pick one using mov esi, 0x40107e[r12] where r12 is either 0xb0 or 0xba ( since mov, 0x123456789 is 10 bytes long).

After finishing off the remaining registers for the write syscall and finding the correct offsets in the binary, it took a couple of goes for the strings to randomly show in the right order but then we get the flag:

RUN ONCE ...
GOT RIGHT RESPONSE!

RUN TWICE ...
THAT WAS NICE!

GG. WE GIVE U FLAG:
tstlss{vdso+vvar_still_delivers_in_2020}

Commented final payload:

# lookup table of 0xb0bab0bab0ba
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;
mov al, 0xba;

# addresses of strings to print
mov r12,0x40114000000000;
mov r12,0x40114a00000000;

# strings to print
mov r12,0x45545341540000;
mov r12,0x215353454C0000;
mov rax, 0;

# load random byte
mov al, byte ptr [0x00007ffff7ffb08b];

# load 0xb0 or 0xba
mov dl, byte ptr 0x401028[rax];
mov r12, rdx;
mov rdi, 1;
mov rdx, 5;

# load the string address
mov esi, 0x40107e[r12];

# string length
mov edx, 5;