Writeup, from: https://github.com/JonathanBeverley , https://github.com/kensalter , https://github.com/BenGardiner
Notes:
- Open challenge binary and analyze it
r2 -d antir2
[...]
[0x004008c0]> aaa
[ ] Analyze all flags starting with sym. and entry0 (aa)
[...]
-
When attempting to debug the program, there is a certain text string "r2 in debug.....hahahaha" printed before force-quitting you
-
I was able to cross reference the strings through a radare / search and then using axt find the point in which its mentioned.
[0x004008c0]> s str.r2_in_debug_mode_won_t_help_you_much__ha_ha_ha......_:__n
[0x004e1c67]> axt
data 0x421208 movabs rdi, str.r2_in_debug_mode_won_t_help_you_much__ha_ha_ha......_:__n in main
data 0x41f9e4 movabs rdi, str.r2_in_debug_mode_won_t_help_you_much__ha_ha_ha......_:__n in main
[0x004e1c67]> s 0x421208
- It seems to be referenced from
main
; the anti-debug printout is called from a jump inmain
at0x418fa6
[0x004e1c67]> axt
data 0x421208 movabs rdi, str.r2_in_debug_mode_won_t_help_you_much__ha_ha_ha......_:__n in main
data 0x41f9e4 movabs rdi, str.r2_in_debug_mode_won_t_help_you_much__ha_ha_ha......_:__n in main
[0x00000000]> s 0x421208
[0x00421208]> axt
code 0x418fa6 je 0x421208 in main
[0x00421208]> s 0x418fa6
- which looks like a million jump statements... they all seem to reference loading [
local_54h
]
[0x00418fa6]> f antidebug_fromhere
[0x00418fa6]> pD 128 @ $$-64~..
│ ; JMP XREF from 0x00418f61 (main)
│ 0x00418f66 8b45ac mov eax, dword [local_54h]
│ 0x00418f69 2dd355df15 sub eax, 0x15df55d3
│ 0x00418f6e 898564ffffff mov dword [local_9ch], eax
│ ┌─< 0x00418f74 0f8431150000 je 0x41a4ab
│ ┌──< 0x00418f7a e900000000 jmp 0x418f7f
│ ││ ; JMP XREF from 0x00418f7a (main)
│ └──> 0x00418f7f 8b45ac mov eax, dword [local_54h]
│ │ 0x00418f82 2d6f9f2b19 sub eax, 0x192b9f6f
│ │ 0x00418f87 898560ffffff mov dword [local_a0h], eax
│ ┌──< 0x00418f8d 0f843c7a0000 je 0x4209cf
│ ┌───< 0x00418f93 e900000000 jmp 0x418f98
│ │││ ; JMP XREF from 0x00418f93 (main)
│ └───> 0x00418f98 8b45ac mov eax, dword [local_54h]
│ ││ 0x00418f9b 2d2f99231a sub eax, 0x1a23992f
│ ││ 0x00418fa0 89855cffffff mov dword [local_a4h], eax
| ┌───< ;-- antidebug_fromhere:
│ ┌───< 0x00418fa6 0f845c820000 je 0x421208
│ ┌────< 0x00418fac e900000000 jmp 0x418fb1
│ ││││ ; JMP XREF from 0x00418fac (main)
│ └────> 0x00418fb1 8b45ac mov eax, dword [local_54h]
│ │││ 0x00418fb4 2dfa6b571d sub eax, 0x1d576bfa
│ │││ 0x00418fb9 898558ffffff mov dword [local_a8h], eax
│ ┌────< 0x00418fbf 0f84cb140000 je 0x41a490
│ ┌─────< 0x00418fc5 e900000000 jmp 0x418fca
│ │││││ ; JMP XREF from 0x00418fc5 (main)
│ └─────> 0x00418fca 8b45ac mov eax, dword [local_54h]
│ ││││ 0x00418fcd 2df3fce31d sub eax, 0x1de3fcf3
│ ││││ 0x00418fd2 898554ffffff mov dword [local_ach], eax
│ ┌─────< 0x00418fd8 0f84b7810000 je 0x421195
│ ┌──────< 0x00418fde e900000000 jmp 0x418fe3
│ ││││││ ; JMP XREF from 0x00418fde (main)
│ └──────> 0x00418fe3 8b45ac mov eax, dword [local_54h]
[0x00418fa6]> afvW~local_54h
local_54h 0x416625,0x418de3,0x418df4,0x418e0a,0x418e20,0x418e36,0x418e4c,0x418e62,0x418e78,0x418e8e,0x418ea4,0x418eba,0x418ed0,0x418ee9,0x418f02,0x418f1b,0x418f34,0x418f4d,0x418f66,0x418f7f,0x418f98,0x418fb1,0x418fca,0x418fe3,0x418ffc,0x419015,0x41902e,0x419047,0x419060,0x419079,0x419092,0x4190ab,0x4190c4,0x4190dd,0x4190f6,0x41910f,0x4231a2
- Inspecting around the function, all are reads, this local is only written to at
0x00418de3
with data from[local_50h]
.
[0x00418fa6]> pdb @ 0x00418dd8
│ ; JMP XREF from 0x00421239 (main)
│ 0x00418dd8 8b45b0 mov eax, dword [local_50h]
│ 0x00418ddb 89c1 mov ecx, eax
│ 0x00418ddd 81e905e77080 sub ecx, 0x8070e705
│ 0x00418de3 8945ac mov dword [local_54h], eax
│ 0x00418de6 894da8 mov dword [local_58h], ecx
│ ┌─< 0x00418de9 0f84e74d0000 je 0x41dbd6
- Attempting to find the end, it looks like this whole function is cyclic. There are 33 jumpXREFs to the bottom. this might be some sort of.... loop? Switch statement?
[0x00418fa6]> pdf
[...]
│ ────────> 0x0042bdb5 c78568ffffff. mov dword [local_98h], 0xd648722e
│ │││││││ ; XREFS: JMP 0x0042bdb0 JMP 0x00427b28 JMP 0x00426458 JMP 0x0042a251 JMP 0x00428b2b JMP 0x0042bd62 JMP 0x0042aebc JMP 0x00427265
│ │││││││ ; XREFS: JMP 0x00427b93 JMP 0x0042b9e0 JMP 0x0042bd92 JMP 0x004274e4 JMP 0x004276df JMP 0x0042bda1 JMP 0x0042bce3 JMP 0x00427b0a
│ │││││││ ; XREFS: JMP 0x00427b51 JMP 0x0042aed9 JMP 0x0042bd48 JMP 0x0042bd19 JMP 0x0042b56b JMP 0x00427bb3 JMP 0x00428f6b JMP 0x0042b55c
│ │││││││ ; XREFS: JMP 0x00427bcd JMP 0x00427b37 JMP 0x00429baa JMP 0x00428f2d JMP 0x00428f1e JMP 0x0042aead JMP 0x0042aef7 JMP 0x00427b6e
│ │││││││ ; XREFS: JMP 0x0042a094 JMP 0x00428f4d JMP 0x0042bd7c JMP 0x0042751e JMP 0x0042a085 JMP 0x004274f3 JMP 0x00428f91 JMP 0x004260f0
│ │││││││ ; XREFS: JMP 0x00426467 JMP 0x0042b2f7 JMP 0x0042549c
│ └└└└└└└─< 0x0042bdbf e9e391ffff jmp 0x424fa7
│ ; JMP XREF from 0x0042bcf8 (main)
│ ────────> 0x0042bdc4 e867d20500 call fcn.00489030
│ 0x0042bdc9 0f1f80000000. nop dword [rax]
│ 0x0042bdd0 b8061d4e00 mov eax, str.aes_partial_ ; 0x4e1d06 ; "aes(partial)"
└ 0x0042bdd5 c3 ret
- How does it know it is being debugged? The message only plays when the
local_54h
is equal to1A23992F
.
[0x00418fa6]> pdb @ 0x00418fa6
│ ; JMP XREF from 0x00418f93 (main)
│ 0x00418f98 8b45ac mov eax, dword [local_54h]
│ 0x00418f9b 2d2f99231a sub eax, 0x1a23992f
│ 0x00418fa0 89855cffffff mov dword [local_a4h], eax
| ┌─< ;-- antidebug_fromhere:
│ ┌─< 0x00418fa6 0f845c820000 je 0x421208
- The magic value
0x1a23992f
shows up only three times; the last is not in code, the first is the test against that value. Let's look at the second
[0x00418fa6]> /v 0x1a23992f
Searching 4 bytes in [0x400000-0x522000]
hits: 3
0x00418f9c hit1_0 2f99231a
0x0041ebbd hit1_1 2f99231a
0x0041fa03 hit1_2 2f99231a
[0x00418fa6]> pd @ hit1_1-5
│ ┌─< 0x0041ebb8 7d26 jge 0x41ebe0
│ │ 0x0041ebba 0000 add byte [rax], al
│ │ ; JMP XREF from 0x00418f5b (main)
│ │ 0x0041ebbc ~ b82f99231a mov eax, 0x1a23992f
| │ ;-- hit1_1:
│ │ 0x0041ebbd 2f invalid ; 0x1a23992f
│ │ 0x0041ebbe 99 cdq
│ │ 0x0041ebbf 231a and ebx, dword [rdx]
[...]
NB: the flag on the immeadiate is breaking the disassembly there, we'll remove flags in the following to avoid this.
-
that value, once loaded into
eax
makes its way magically tolocal_54h
(the EBB is very very long) -
the above is only called is
local_54h
is equal to0xae2d291
[0x004008c0]> s 0x00418f5b
[0x00418f5b]> pd
[0x00418f5b]> pdb
│ ; JMP XREF from 0x00418f48 (main)
│ 0x00418f4d 8b45ac mov eax, dword [local_54h]
│ 0x00418f50 2d91d2e20a sub eax, 0xae2d291
│ 0x00418f55 898568ffffff mov dword [local_98h], eax
│ ┌─< 0x00418f5b 0f845b5c0000 je 0x41ebbc
- which is called only if
local_54h
is not0x94524fa
│ │ ; JMP XREF from 0x00418f2f (main)
│ └─> 0x00418f34 8b45ac mov eax, dword [local_54h]
│ 0x00418f37 2dfa244509 sub eax, 0x94524fa
│ 0x00418f3c 89856cffffff mov dword [local_94h], eax
│ ┌─< 0x00418f42 0f84e2810000 je 0x42112a ;[2]
│ ┌──< 0x00418f48 e900000000 jmp 0x418f4d ;[3]
-
and so on, testing for various other values of
local_54h
that it is not. We'll focus on the test for the concrete value0xae2d291
-
the value
0xae2d291
whichlocal_54h
must attain is written tolocal_50h
whenlocal_34h
is6
[0x0041eba7]> /v 0xae2d291
Searching 4 bytes in [0x400000-0x522000]
hits: 2
0x00418f51 hit3_0 91d2e20a
0x0041eba7 hit3_1 91d2e20a
[0x0041eba7]> s 0x0041eba7
[0x0041eba7]> f-hit*
[0x0041eba7]> pdb
│ ; JMP XREF from 0x00418eaf (main)
│ 0x0041eba1 b82f82de24 mov eax, 0x24de822f
│ 0x0041eba6 b991d2e20a mov ecx, 0xae2d291
│ 0x0041ebab 8b55cc mov edx, dword [local_34h]
│ 0x0041ebae 83fa06 cmp edx, 6 ; 6
│ 0x0041ebb1 0f45c1 cmovne eax, ecx
│ 0x0041ebb4 8945b0 mov dword [local_50h], eax
│ ┌─< 0x0041ebb7 e97d260000 jmp 0x421239
- it's not clear how that would also propagate to
local_54h
-- but let's just roll with it. Assuming it will, then if we prevent the magic value0xae2d291
from getting intolocal_50h
, we will prevent detection of debugging. So we can patch-out the conditional mov,cmovne
at0x41ebb1
[0x0041eba7]> s 0x0041ebb1
[0x0041ebb1]> "wa nop;nop;nop"
Written 3 bytes (nop;nop;nop) = wx 909090
[0x0041ebb1]> pdb
│ ; JMP XREF from 0x00418eaf (main)
│ 0x0041eba1 b82f82de24 mov eax, 0x24de822f
│ 0x0041eba6 b991d2e20a mov ecx, 0xae2d291
│ 0x0041ebab 8b55cc mov edx, dword [local_34h]
│ 0x0041ebae 83fa06 cmp edx, 6 ; 6
│ 0x0041ebb1 90 nop
│ 0x0041ebb2 90 nop
│ 0x0041ebb3 90 nop
│ 0x0041ebb4 8945b0 mov dword [local_50h], eax
│ ┌─< 0x0041ebb7 e97d260000 jmp 0x421239
[0x0041ebb1]>
- yay! we patched-out the anti-debug check!
[0x0041ebb1]> dc
child stopped with signal 28
[+] SIGNAL 28 errno=0 addr=0x00000000 code=128 ret=0
got signal...
[0x004008c0]> dc
*******************************************
* Radare2con 2017 rhme3 pre-quals edition *
*******************************************
r<< Can you r2 me?
Plaintext : 47 65 74 20 74 68 65 20 61 65 73 6B 65 79 21 21
Encrypted : FD DA 9B 78 FC F8 E9 BF 33 72 6E 0A 8A E5 F6 8C
Decrypted : 47 65 74 20 74 68 65 20 61 65 73 6B 65 79 21 21
[0x00484b88]>
- Let's restart that debug session and plan a breakpoint. Working backwards from the "Decrypted" string
[0x0041ebb1]> s str.Decrypted
[0x004e1bc9]> axt
data 0x400c97 mov eax, str.Decrypted in main
[0x004e1bc9]> s 0x400c97
[0x00400c97]> pdb
[...]
│ 0x00400c34 e857c10200 call fcn.0042cd90
│ 0x00400c39 b8a01b4e00 mov eax, 0x4e1ba0
│ 0x00400c3e 89c7 mov edi, eax
│ 0x00400c40 8b8564fdffff mov eax, dword [local_29ch]
│ 0x00400c46 4188c3 mov r11b, al
│ 0x00400c49 4488d8 mov al, r11b
│ 0x00400c4c e8bfad0400 call fcn.0044ba10
│ 0x00400c51 41b9b51b4e00 mov r9d, str.Plaintext ; 0x4e1bb5 ; "Plaintext"
│ 0x00400c57 4489cf mov edi, r9d
│ 0x00400c5a 41b9901b4e00 mov r9d, 0x4e1b90
│ 0x00400c60 4489ce mov esi, r9d
│ 0x00400c63 41b910000000 mov r9d, 0x10 ; 16
│ 0x00400c69 4489ca mov edx, r9d
│ 0x00400c6c 898550fdffff mov dword [local_2b0h], eax
│ 0x00400c72 44898d4cfdff. mov dword [local_2b4h], r9d
│ 0x00400c79 e8c20a0100 call 0x411740
│ 0x00400c7e b8bf1b4e00 mov eax, str.Encrypted ; 0x4e1bbf ; "Encrypted"
│ 0x00400c83 89c7 mov edi, eax
│ 0x00400c85 488bb568fdff. mov rsi, qword [local_298h]
│ 0x00400c8c 8b954cfdffff mov edx, dword [local_2b4h]
│ 0x00400c92 e8a90a0100 call 0x411740
│ 0x00400c97 b8c91b4e00 mov eax, str.Decrypted ; 0x4e1bc9 ; "Decrypted"
│ 0x00400c9c 89c7 mov edi, eax
│ 0x00400c9e 488bb558fdff. mov rsi, qword [local_2a8h]
│ 0x00400ca5 8b954cfdffff mov edx, dword [local_2b4h]
│ 0x00400cab e8900a0100 call 0x411740
│ 0x00400cb0 64488b0c2528. mov rcx, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
│ 0x00400cb9 488b75f8 mov rsi, qword [local_8h]
│ 0x00400cbd 4839f1 cmp rcx, rsi
│ ┌─< 0x00400cc0 0f8510000000 jne 0x400cd6
- The first call to
0x411740
seems like a good spot.
[0x00400c97]> db 0x00400c79
[0x00400c97]> dc
child stopped with signal 28
[+] SIGNAL 28 errno=0 addr=0x00000000 code=128 ret=0
got signal...
[0x004008c0]> dc
*******************************************
* Radare2con 2017 rhme3 pre-quals edition *
*******************************************
r<< Can you r2 me?
hit breakpoint at: 400c79
[0x00400c79]>
- Let's check the stack
[0x00400c79]> ad 10 @rsp
0x7fff1ecc6bb0 0000000000000000 (null)
0x7fff1ecc6bb8 0000000010000000 pointer 0x1000000000
`- 0x1000000000 ffffffffffffffff invalid
0x7fff1ecc6bc0 1400000000000000 pointer 0x00000014
`- 0x00000014 ffffffffffffffff invalid
0x7fff1ecc6bc8 206ecc1eff7f0000 pointer 0x7fff1ecc6e20
`- 0x7fff1ecc6e20 4765742074686520 pointer 0x2065687420746547
`- 0x7fff1ecc6bd0 0000000000000000 (null)
0x7fff1ecc6bd8 306ecc1eff7f0000 pointer 0x7fff1ecc6e30
`- 0x7fff1ecc6e30 fdda9b78fcf8e9bf pointer 0xbfe9f8fc789bdafd
`- 0x7fff1ecc6be0 306ccc1eff7f0000 pointer 0x7fff1ecc6c30
`- 0x7fff1ecc6c30 7ccdaca5e94484fb pointer 0xfb8444e9a5accd7c
`- 0x7fff1ecc6be8 4500000000000000 pointer 0x00000045
`- 0x00000045 ffffffffffffffff invalid
0x7fff1ecc6bf0 00000000a9085105 pointer 0x55108a900000000
`- 0x55108a900000000 ffffffffffffffff invalid
0x7fff1ecc6bf8 0000000086aa782f pointer 0x2f78aa8600000000
`- 0x2f78aa8600000000 ffffffffffffffff invalid
- we recognize
4765742074686520
as the plaintext. I wonder what is around there?
[0x00400c79]> px @ 0x7fff1ecc6e20
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
0x7fff1ecc6e20 4765 7420 7468 6520 6165 736b 6579 2121 Get the aeskey!!
0x7fff1ecc6e30 fdda 9b78 fcf8 e9bf 3372 6e0a 8ae5 f68c ...x....3rn.....
0x7fff1ecc6e40 fdda 9b78 fcf8 e9bf 3372 6e0a 8ae5 f68c ...x....3rn.....
0x7fff1ecc6e50 7261 6461 7265 3263 6f6e 3465 7665 7221 radare2con4ever!
0x7fff1ecc6e60 60db 4300 0000 0000 003f 3c3c d183 316e `.C......?<<..1n
0x7fff1ecc6e70 1820 7200 0000 0000 46d2 4300 0000 0000 . r.....F.C.....
0x7fff1ecc6e80 0000 0000 0000 0000 0000 0000 0100 0000 ................
0x7fff1ecc6e90 b86f cc1e ff7f 0000 e009 4000 0000 0000 .o........@.....
0x7fff1ecc6ea0 b802 4000 0000 0000 4f84 4b96 1557 d8fe ..@.....O.K..W..
0x7fff1ecc6eb0 60db 4300 0000 0000 f0db 4300 0000 0000 `.C.......C.....
0x7fff1ecc6ec0 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x7fff1ecc6ed0 4f84 7b0b 696a 2601 4f84 7972 7657 d8fe O.{.ij&.O.yrvW..
0x7fff1ecc6ee0 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x7fff1ecc6ef0 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x7fff1ecc6f00 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x7fff1ecc6f10 bb07 0000 0000 0000 b86f cc1e ff7f 0000 .........o......
- EYYYYYYYYYYYYYYYYYYYYYYYY hfound it.
$echo -n 'Get the aeskey!!' | openssl aes-128-ecb -e -nopad -nosalt -K $(echo -n 'radare2con4ever!' | xxd -ps) | xxd -u -g1
00000000: FD DA 9B 78 FC F8 E9 BF 33 72 6E 0A 8A E5 F6 8C ...x....3rn.....