movcode - Tasteless CTF 2020
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
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;