Tags: local rev forensics
Rating:
# FE-CTF 2022: Cyber Demon
# Challenge: Coredump
## Tags
`forensics`, `local`
## Unarchiving the file
The challenge is delivered in the form of a TAR archive. I unarchived the tar-file using the following command:
```
$ tar -xvf coredump-fa5c93319608bd3e66093844ca0fa457bdbfd559.tar
coredump/core
```
The tar-file contains a single file.
## Determining the file type
I used the `file`-command to determine the type of file like shown below:
```
$ file core
core: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from './coredump', real uid: 1000, effective uid: 1000, real gid: 1000, effective gid: 1000, execfn: './coredump', platform: 'x86_64'
```
The file `core` is a core dump.
"*In computing, a core dump consists of the recorded state of the working memory of a computer program at a specific time, generally when the program has crashed or otherwise terminated abnormally.*"
-- [https://en.wikipedia.org/wiki/Core_dump](https://en.wikipedia.org/wiki/Core_dump)
## Analyzing the file using GDB
I loaded the coredump using GDB with the following commands:
```
$ gdb
(gdb) core ./core
[New LWP 22]
Core was generated by `./coredump'.
Program terminated with signal SIGTRAP, Trace/breakpoint trap.
#0 0x000055ff00686244 in ?? ()
```
The program terminated with a "`signal SIGTRAP, Trace/breakpoint trap`".
"*The SIGTRAP signal is sent to a process when an exception (or trap) occurs: a condition that a debugger has requested to be informed of – for example, when a particular function is executed, or when a particular variable changes value.*"
-- [https://en.wikipedia.org/wiki/Signal_(IPC)#SIGTRAP](https://en.wikipedia.org/wiki/Signal_(IPC)#SIGTRAP)
The breakpoint at the Intel architecture is the `int3`-instruction, which is a one byte opcode (`0xcc`), which the debugger overwrites the original program with, when a breakpoint is set.
I used the following commands in `gdb` to see the breakpoint instruction and the two instructions following the breakpoint:
```
(gdb) set disassembly-flavor intel
(gdb) x/3i $rip-1
0x55ff00686243: int3
=> 0x55ff00686244: mov eax,0x0
0x55ff00686249: call 0x55ff00686125
```
The "`=>`" in the output of GDB shows the current Program Counter/Instruction Pointer (`$pc` or in this case also `$rip`) .
The `call`-instruction is used when calling a function. I used the following command to look at the function located at the memory address `0x55ff00686125`:
```
(gdb) x/74i 0x55ff00686125
0x55ff00686125: push rbp
0x55ff00686126: mov rbp,rsp
0x55ff00686129: movabs rax,0x2d30053933293432
0x55ff00686133: movabs rdx,0x3138622d3026623a
0x55ff0068613d: mov QWORD PTR [rbp-0x30],rax
0x55ff00686141: mov QWORD PTR [rbp-0x28],rdx
0x55ff00686145: movabs rax,0x623a316221653835
0x55ff0068614f: movabs rdx,0x2e31202f622d3026
0x55ff00686159: mov QWORD PTR [rbp-0x20],rax
0x55ff0068615d: mov QWORD PTR [rbp-0x18],rdx
0x55ff00686161: mov DWORD PTR [rbp-0x10],0x3f293562
0x55ff00686168: mov WORD PTR [rbp-0xc],0x48
0x55ff0068616e: mov DWORD PTR [rbp-0x4],0x0
0x55ff00686175: jmp 0x55ff00686212
0x55ff0068617a: mov eax,DWORD PTR [rbp-0x4]
0x55ff0068617d: cdqe
0x55ff0068617f: movzx eax,BYTE PTR [rbp+rax*1-0x30]
0x55ff00686184: mov BYTE PTR [rbp-0x5],al
0x55ff00686187: xor BYTE PTR [rbp-0x5],0x42
0x55ff0068618b: cmp BYTE PTR [rbp-0x5],0x40
0x55ff0068618f: jle 0x55ff006861c5
0x55ff00686191: cmp BYTE PTR [rbp-0x5],0x5a
0x55ff00686195: jg 0x55ff006861c5
0x55ff00686197: movsx eax,BYTE PTR [rbp-0x5]
0x55ff0068619b: sub eax,0x64
0x55ff0068619e: movsxd rdx,eax
0x55ff006861a1: imul rdx,rdx,0x4ec4ec4f
0x55ff006861a8: shr rdx,0x20
0x55ff006861ac: sar edx,0x3
0x55ff006861af: mov ecx,eax
0x55ff006861b1: sar ecx,0x1f
0x55ff006861b4: sub edx,ecx
0x55ff006861b6: imul ecx,edx,0x1a
0x55ff006861b9: sub eax,ecx
0x55ff006861bb: mov edx,eax
0x55ff006861bd: mov eax,edx
0x55ff006861bf: add eax,0x5a
0x55ff006861c2: mov BYTE PTR [rbp-0x5],al
0x55ff006861c5: cmp BYTE PTR [rbp-0x5],0x60
0x55ff006861c9: jle 0x55ff00686201
0x55ff006861cb: cmp BYTE PTR [rbp-0x5],0x7a
0x55ff006861cf: jg 0x55ff00686201
0x55ff006861d1: movsx eax,BYTE PTR [rbp-0x5]
0x55ff006861d5: sub eax,0x84
0x55ff006861da: movsxd rdx,eax
0x55ff006861dd: imul rdx,rdx,0x4ec4ec4f
0x55ff006861e4: shr rdx,0x20
0x55ff006861e8: sar edx,0x3
0x55ff006861eb: mov ecx,eax
0x55ff006861ed: sar ecx,0x1f
0x55ff006861f0: sub edx,ecx
0x55ff006861f2: imul ecx,edx,0x1a
0x55ff006861f5: sub eax,ecx
0x55ff006861f7: mov edx,eax
0x55ff006861f9: mov eax,edx
0x55ff006861fb: add eax,0x7a
0x55ff006861fe: mov BYTE PTR [rbp-0x5],al
0x55ff00686201: mov eax,DWORD PTR [rbp-0x4]
0x55ff00686204: cdqe
0x55ff00686206: movzx edx,BYTE PTR [rbp-0x5]
0x55ff0068620a: mov BYTE PTR [rbp+rax*1-0x30],dl
---Type <return> to continue, or q <return> to quit---
0x55ff0068620e: add DWORD PTR [rbp-0x4],0x1
0x55ff00686212: mov eax,DWORD PTR [rbp-0x4]
0x55ff00686215: cmp eax,0x24
0x55ff00686218: jbe 0x55ff0068617a
0x55ff0068621e: lea rax,[rbp-0x30]
0x55ff00686222: mov rsi,rax
0x55ff00686225: mov rax,0x1
0x55ff0068622c: mov rdi,0x1
0x55ff00686233: mov rdx,0x25
0x55ff0068623a: syscall
0x55ff0068623c: nop
0x55ff0068623d: pop rbp
0x55ff0068623e: ret
```
The function at memory address `0x55ff00686125` stores an obfuscated string on the stack, deobfuscates the string and then writes the deobfuscated string to standard output.
## The solution in GNU Assembler
I wrote a simple assembly skeleton, and included the function I found at memory address `0x55ff00686125` as well as the call to said function.
The only thing I changed in the function found at memory address `0x55ff00686125`, before including it in the simple assembly skeleton, was the memory addresses for `call` and jumps (`jmp`, `jle`, `jg`, `jbe`, etc.). I changed those to labels, and the assembly source code ended up looking like the following:
```
.intel_syntax noprefix
.global _start
.text
_start:
mov eax,0x0
call func1
# exit(0)
push 0x3c # system call 60 is exit
pop rax
xor rdi,rdi # we want return code 0
syscall # invoke operating system to exit
func1:
push rbp
mov rbp,rsp
movabs rax,0x2d30053933293432
movabs rdx,0x3138622d3026623a
mov QWORD PTR [rbp-0x30],rax
mov QWORD PTR [rbp-0x28],rdx
movabs rax,0x623a316221653835
movabs rdx,0x2e31202f622d3026
mov QWORD PTR [rbp-0x20],rax
mov QWORD PTR [rbp-0x18],rdx
mov DWORD PTR [rbp-0x10],0x3f293562
mov WORD PTR [rbp-0xc],0x48
mov DWORD PTR [rbp-0x4],0x0
jmp loop_condition
loop_body:
mov eax,DWORD PTR [rbp-0x4]
cdqe
movzx eax,BYTE PTR [rbp+rax*1-0x30]
mov BYTE PTR [rbp-0x5],al
xor BYTE PTR [rbp-0x5],0x42
cmp BYTE PTR [rbp-0x5],0x40
jle second_check
cmp BYTE PTR [rbp-0x5],0x5a
jg second_check
movsx eax,BYTE PTR [rbp-0x5]
sub eax,0x64
movsxd rdx,eax
imul rdx,rdx,0x4ec4ec4f
shr rdx,0x20
sar edx,0x3
mov ecx,eax
sar ecx,0x1f
sub edx,ecx
imul ecx,edx,0x1a
sub eax,ecx
mov edx,eax
mov eax,edx
add eax,0x5a
mov BYTE PTR [rbp-0x5],al
second_check:
cmp BYTE PTR [rbp-0x5],0x60
jle deobfuscate_flag
cmp BYTE PTR [rbp-0x5],0x7a
jg deobfuscate_flag
movsx eax,BYTE PTR [rbp-0x5]
sub eax,0x84
movsxd rdx,eax
imul rdx,rdx,0x4ec4ec4f
shr rdx,0x20
sar edx,0x3
mov ecx,eax
sar ecx,0x1f
sub edx,ecx
imul ecx,edx,0x1a
sub eax,ecx
mov edx,eax
mov eax,edx
add eax,0x7a
mov BYTE PTR [rbp-0x5],al
deobfuscate_flag:
mov eax,DWORD PTR [rbp-0x4]
cdqe
movzx edx,BYTE PTR [rbp-0x5]
mov BYTE PTR [rbp+rax*1-0x30],dl
add DWORD PTR [rbp-0x4],0x1
loop_condition:
mov eax,DWORD PTR [rbp-0x4]
cmp eax,0x24
jbe loop_body
lea rax,[rbp-0x30]
mov rsi,rax
mov rax,0x1
mov rdi,0x1
mov rdx,0x25
syscall
nop
pop rbp
ret
```
I compiled and linked the assembly source code using the following commands:
```
$ gcc -c coredump_solution.s
$ ld -o coredump_solution coredump_solution.o
```
## The flag
When you run the executable, it will write the flag to the standard output like shown below:
```
$ ./coredump_solution
flag{When the pimp's in the crib ma}
```