unpackme

Challenge

Can you get the flag? Reverse engineer this binary.

Solution

The challenge strongly hints (it's in the binary name, challenge name, and is a literal hint) that this is a binary packed with UPX. Additionally, running strings unpackme-upx | grep upx displays Info: This file is packed with the UPX executable packer http://upx.sf.net $. According to their website, "UPX is a free, portable, extendable, high-performance executable packer for several executable formats."
We can download the latest release of UPX from the GitHub releases page. Extracting the archive gives us a folder with a upx binary. We can run ./upx -d unpackme-upx to decompress the binary and replace it on disk. Now, our unpackme-upx file is unpacked.
Next, run gdb ./unpackme-upx and layout asm (or disassemble main) to get:
0x0000000000401e73 <+0>: endbr64
0x0000000000401e77 <+4>: push %rbp
0x0000000000401e78 <+5>: mov %rsp,%rbp
0x0000000000401e7b <+8>: sub $0x50,%rsp
0x0000000000401e7f <+12>: mov %edi,-0x44(%rbp)
0x0000000000401e82 <+15>: mov %rsi,-0x50(%rbp)
0x0000000000401e86 <+19>: mov %fs:0x28,%rax
0x0000000000401e8f <+28>: mov %rax,-0x8(%rbp)
0x0000000000401e93 <+32>: xor %eax,%eax
0x0000000000401e95 <+34>: movabs $0x4c75257240343a41,%rax
0x0000000000401e9f <+44>: movabs $0x30623e306b6d4146,%rdx
0x0000000000401ea9 <+54>: mov %rax,-0x30(%rbp)
0x0000000000401ead <+58>: mov %rdx,-0x28(%rbp)
0x0000000000401eb1 <+62>: movabs $0x3532666630486637,%rax
0x0000000000401ebb <+72>: mov %rax,-0x20(%rbp)
0x0000000000401ebf <+76>: movl $0x36665f60,-0x18(%rbp)
0x0000000000401ec6 <+83>: movw $0x4e,-0x14(%rbp)
0x0000000000401ecc <+89>: lea 0xb1131(%rip),%rdi # 0x4b3004
0x0000000000401ed3 <+96>: mov $0x0,%eax
0x0000000000401ed8 <+101>: call 0x410df0 <printf>
0x0000000000401edd <+106>: lea -0x3c(%rbp),%rax
0x0000000000401ee1 <+110>: mov %rax,%rsi
0x0000000000401ee4 <+113>: lea 0xb1135(%rip),%rdi # 0x4b3020
0x0000000000401eeb <+120>: mov $0x0,%eax
0x0000000000401ef0 <+125>: call 0x410f80 <__isoc99_scanf>
0x0000000000401ef5 <+130>: mov -0x3c(%rbp),%eax
0x0000000000401ef8 <+133>: cmp $0xb83cb,%eax
0x0000000000401efd <+138>: jne 0x401f42 <main+207>
0x0000000000401eff <+140>: lea -0x30(%rbp),%rax
0x0000000000401f03 <+144>: mov %rax,%rsi
0x0000000000401f06 <+147>: mov $0x0,%edi
0x0000000000401f0b <+152>: call 0x401db5 <rotate_encrypt>
0x0000000000401f10 <+157>: mov %rax,-0x38(%rbp)
0x0000000000401f14 <+161>: mov 0xdd7b5(%rip),%rdx # 0x4df6d0 <stdout>
0x0000000000401f1b <+168>: mov -0x38(%rbp),%rax
0x0000000000401f1f <+172>: mov %rdx,%rsi
0x0000000000401f22 <+175>: mov %rax,%rdi
0x0000000000401f25 <+178>: call 0x420bd0 <fputs>
0x0000000000401f2a <+183>: mov $0xa,%edi
0x0000000000401f2f <+188>: call 0x421070 <putchar>
0x0000000000401f34 <+193>: mov -0x38(%rbp),%rax
0x0000000000401f38 <+197>: mov %rax,%rdi
0x0000000000401f3b <+200>: call 0x42eec0 <free>
0x0000000000401f40 <+205>: jmp 0x401f4e <main+219>
0x0000000000401f42 <+207>: lea 0xb10da(%rip),%rdi # 0x4b3023
0x0000000000401f49 <+214>: call 0x420e90 <puts>
0x0000000000401f4e <+219>: mov $0x0,%eax
0x0000000000401f53 <+224>: mov -0x8(%rbp),%rcx
0x0000000000401f57 <+228>: xor %fs:0x28,%rcx
0x0000000000401f60 <+237>: je 0x401f67 <main+244>
0x0000000000401f62 <+239>: call 0x45cdf0 <__stack_chk_fail_local>
0x0000000000401f67 <+244>: leave
0x0000000000401f68 <+245>: ret
We know that the program asks for a number and then will probably print the flag given the correct number. So, we are looking for a cmp instruction. We see <+133>: cmp $0xb83cb,%eax. Converting b83cb from hexadecimal to decimal yields 754635.
Running the program and entering 754635 prints out the flag.
Note that without decompressing using upx, gdb would have been unable to display any assembly.

Flag

picoCTF{up><_m3_f7w_77ad107e}
Copy link
Edit on GitHub