AUCTF writeup - plain_jane

In AUCTF 2020 under rev section, the challenge plain jane had a assembly code. It said, we need to figure out what the program returns.

Here's what I did.

First, compile it to binary using gcc

Compile

gcc -o plain_jane plain_jane.s

Then open the binary in radare.

Recon

o plain_jane
aaa
i
fd       7
file     plain_jane
size     0x4040
humansz  16.1K
mode     r-x
format   elf64
iorw     false
blksz    0x0
block    0x100
type     DYN (Shared object file)
arch     x86
baddr    0x0
binsz    14714
bintype  elf
bits     64
canary   false
class    ELF64
compiler GCC: (Arch Linux 9.3.0-1) 9.3.0/GCC: (Debian 9.2.1-22) 9.2.1 20200104
crypto   false
endian   little
havecode true
intrp    /lib64/ld-linux-x86-64.so.2
laddr    0x0
lang     c
linenum  true
lsyms    true
machine  AMD x86-64 architecture
maxopsz  16
minopsz  1
nx       true
os       linux
pcalign  0
pic      true
relocs   true
relro    partial
rpath    NONE
sanitiz  false
static   false
stripped false
subsys   linux
va       true
aflm
entry0:
    reloc.__libc_start_main

entry.fini0:
    reloc.__cxa_finalize
    rip

sym.__libc_csu_init:
    sym._init
    rdx

main:
    sym.func_1
    sym.func_2
    sym.func_3

So we have main calling 3 functions func_1, func_2 and func_3

s main
pdf
┌ 59: int main (int argc, char **argv, char **envp);
│           ; var int64_t var_ch @ rbp-0xc
│           ; var int64_t var_8h @ rbp-0x8
│           ; var int64_t var_4h @ rbp-0x4
│           ; DATA XREF from entry0 @ 0x1041
│           0x00001119      55             push rbp
│           0x0000111a      4889e5         mov rbp, rsp
│           0x0000111d      4883ec10       sub rsp, 0x10
│           0x00001121      b800000000     mov eax, 0
│           0x00001126      e829000000     call sym.func_1
│           0x0000112b      8945fc         mov dword [var_4h], eax
│           0x0000112e      b800000000     mov eax, 0
│           0x00001133      e85e000000     call sym.func_2
│           0x00001138      8945f8         mov dword [var_8h], eax
│           0x0000113b      8b55f8         mov edx, dword [var_8h]
│           0x0000113e      8b45fc         mov eax, dword [var_4h]
│           0x00001141      89d6           mov esi, edx
│           0x00001143      89c7           mov edi, eax
│           0x00001145      e85c000000     call sym.func_3
│           0x0000114a      8945f4         mov dword [var_ch], eax
│           0x0000114d      b800000000     mov eax, 0
│           0x00001152      c9             leave
└           0x00001153      c3             ret

Disassembly of main shows us these calls being made and the parameters passed.

Now since we need to know what the program returns, find the location where the final output of the program is stored. The final computation is returned by the func_3 fn call, where the output is pushed to stack at rbp-0xc.

Running the program in debugger and setting a breakpoint at 0x0000114a should give us the output we have been looking for.

Debug to get the flag

ood
s main
pdf
7380
┌ 59: int main (int argc, char **argv, char **envp);
│           ; var int64_t var_ch @ rbp-0xc
│           ; var int64_t var_8h @ rbp-0x8
│           ; var int64_t var_4h @ rbp-0x4
│           ; DATA XREF from entry0 @ 0x55c1cd642041
│           0x55c1cd642119      55             push rbp
│           0x55c1cd64211a      4889e5         mov rbp, rsp
│           0x55c1cd64211d      4883ec10       sub rsp, 0x10
│           0x55c1cd642121      b800000000     mov eax, 0
│           0x55c1cd642126      e829000000     call sym.func_1
│           0x55c1cd64212b      8945fc         mov dword [var_4h], eax
│           0x55c1cd64212e      b800000000     mov eax, 0
│           0x55c1cd642133      e85e000000     call sym.func_2
│           0x55c1cd642138      8945f8         mov dword [var_8h], eax
│           0x55c1cd64213b      8b55f8         mov edx, dword [var_8h]
│           0x55c1cd64213e      8b45fc         mov eax, dword [var_4h]
│           0x55c1cd642141      89d6           mov esi, edx
│           0x55c1cd642143      89c7           mov edi, eax
│           0x55c1cd642145      e85c000000     call sym.func_3
│           0x55c1cd64214a      8945f4         mov dword [var_ch], eax
│           0x55c1cd64214d      b800000000     mov eax, 0
│           0x55c1cd642152      c9             leave
└           0x55c1cd642153      c3             ret
dcu 0x55c1cd64214a

At this state, the value returned from func_3 is stored in eax register. Let's inspect the registers.

dr
rax = 0x00006fcf
rbx = 0x55c1cd642250
rcx = 0x7fa9d9097578
rdx = 0xffffffb6
r8 = 0x00000000
r9 = 0x7fa9d90d5260
r10 = 0x00000003
r11 = 0x00000002
r12 = 0x55c1cd642020
r13 = 0x7fff4914ae50
r14 = 0x00000000
r15 = 0x00000000
rsi = 0x000000cf
rdi = 0x00000042
rsp = 0x7fff4914ad50
rbp = 0x7fff4914ad60
rip = 0x55c1cd64214a
rflags = 0x00000246
orax = 0xffffffffffffffff

In x86_64, the rax register's lower half i.e the 32bits from LSB is the value stored in eax. On converting 0x6fcf into a decimal, we should get the flag.

? 0x6fcf
int32   28623
uint32  28623
hex     0x6fcf
octal   067717
unit    28.0K
segment 0000:0fcf
string  "\xcfo"
fvalue: 28623.0
float:  0.000000f
double: 0.000000
binary  0b0110111111001111
trits   0t1110021010

The flag is 28623