Black Alps was a nice conference where I gave yet another talk about Snuffleupagus (you can get our slides here, those time including Winnie the Pooh), and happened to have a pretty amazing badge:

The badge was of course full of flag to find, some by "manually fuzzing" the menus, others by playing with bluetooth, … We spent some time on it, but apparently, the USB-to-serial thingy was "oddly wired", preventing us from dumping the firmware with my phone charger cable, and I didn't have anything fancier, like an UART in my pocket.
So I focused on doing the game2, which was about reversing, that you could
play by connecting to the badge with serial-over-usb
minicom --device /dev/ttyUSB0 Welcome to minicom 2.7 OPTIONS: I18n Compiled on Feb 7 2016, 13:37:27. Port /dev/ttyUSB0, 14:45:34 Press CTRL-A Z for help on special keys > help show Show information stat Show cow statistics name Set cow name game1 Can you find the password ? game2 Reversing challenge game3 Can you spot the cheat code ? help Show help on available commands > game2 Missing argument. > game2 hunter2 Here is my password checking function. Have fun ! 3641008202003c19979859920206a2a061a79954a202073b99979a50a20209920205979a4b2ba8a7994a9202023b8887994692020a82a0788799419202084c8887993d8202014c39979839a202032cd99098309090 Incorrect password :( >
The chip used is an ESP-WROOM-32, using the xtensa cpu architecture, and of course, radare2 supports it:
$ rasm2 -L | grep xtensa _dAe 32 xtensa GPL3 XTensa CPU $
I could have done the challenge only with rasm2, but it's funnier with
radare2:
$ r2 -a xtensa - [0x00000000]> wx 3641008202003c19979859920206a2a061a79954a202073b99979a50a20209920205979a4b2ba8a7994a9202023b8887994692020a82a0788799419202084c8887993d8202014c39979839a202032cd9909830909074979a2f220204fb8887122b0c021df00c021df00c021df00c021df00c021df00c021df00c021df00c021df00c021df00c021df00c021df00c121df0 [0x00000000]> aaaaa [x] Analyze all flags starting with sym. and entry0 (aa) [x] Analyze len bytes of instructions for references (aar) [x] Analyze function calls (aac) [x] Emulate code to find computed references (aae) [x] Analyze consecutive function (aat) [x] Constructing a function name for fcn.* and sym.func.* functions (aan) [x] Type matching analysis for all functions (afta) [0x00000000]> VV __0x0__ │ │ │ └─┐ ┌─────┘ │ │ │ __0xb__ __0x65__ │ │ ┌─────┘ └─┐ │ │ __0x14__ __0x69__ │ │ ┌─────┘ └─┐ │ │ __0x1c__ __0x6d__ │ │ ┌─────┘ └─┐ │ │ __0x25__ __0x71__ │ │ ┌─────┘ └─┐ │ │ __0x2a__ __0x75__ │ │ ┌─────┘ └─┐ │ │ __0x32__ __0x79__ │ │ ┌─────┘ └─┐ │ │ __0x3b__ __0x7d__ │ │ ┌─────┘ └─┐ │ │ __0x43__ __0x81__ │ │ ┌─────┘ └─┐ │ │ __0x4b__ __0x85__ │ │ ┌─────┘ └─┐ │ │ __0x59__ __0x89__ │ │ ┌─────┘ └─┐ │ │ __0x61__ __0x8d__
It look like a chain of check for each of the eleven characters of the password.
[0x00000065]> pdf ┌ (fcn) fcn.00000000 145 │ 0x00000000 364100 entry a1, 32 │ 0x00000001 410082 l32r a4, 0xfffe0804 │ 0x00000004 02003c l8ui a0, a0, 60 │ 0x00000007 1997 s32i.n a1, a7, 36 │ 0x00000009 9859 l32i.n a9, a9, 20 │ 0x0000000b 920206 l8ui a9, a2, 6 │ 0x0000000e a2a061 movi a10, 97 │ 0x00000011 a79954 bne a9, a10, 0x00000069 │ 0x00000014 a20207 l8ui a10, a2, 7 │ 0x00000017 3b99 addi.n a9, a9, 3 │ 0x00000019 979a50 bne a10, a9, 0x0000006d │ 0x0000001c a20209 l8ui a10, a2, 9 │ 0x0000001f 920205 l8ui a9, a2, 5 │ 0x00000022 979a4b bne a10, a9, 0x00000071 │ 0x00000025 2ba8 addi.n a10, a8, 2 │ 0x00000027 a7994a bne a9, a10, 0x00000075 │ 0x0000002a 920202 l8ui a9, a2, 2 │ 0x0000002d 3b88 addi.n a8, a8, 3 │ 0x0000002f 879946 bne a9, a8, 0x00000079 │ 0x00000032 92020a l8ui a9, a2, 10 │ 0x00000035 82a078 movi a8, 120 │ 0x00000038 879941 bne a9, a8, 0x0000007d │ 0x0000003b 920208 l8ui a9, a2, 8 │ 0x0000003e 4c88 movi.n a8, 72 │ 0x00000040 87993d bne a9, a8, 0x00000081 │ 0x00000043 820201 l8ui a8, a2, 1 │ 0x00000046 4c39 movi.n a9, 67 │ 0x00000048 979839 bne a8, a9, 0x00000085 │ 0x0000004b a20203 l8ui a10, a2, 3 │ 0x0000004e 2cd9 movi.n a9, 45 │ 0x00000050 909830 xor a9, a8, a9 │ 0x00000053 909074 extui a9, a9, 0, 8 │ 0x00000056 979a2f bne a10, a9, 0x00000089 │ 0x00000059 220204 l8ui a2, a2, 4 │ 0x0000005c fb88 addi.n a8, a8, 15 │ 0x0000005e 87122b beq a2, a8, 0x0000008d │ 0x00000061 0c02 movi.n a2, 0 │ 0x00000063 1df0 retw.n │ 0x00000065 0c02 movi.n a2, 0 │ 0x00000067 1df0 retw.n │ 0x00000069 0c02 movi.n a2, 0 │ 0x0000006b 1df0 retw.n │ 0x0000006c f00c02 andb b0, b12, b15 │ 0x0000006f 1df0 retw.n │ 0x00000071 0c02 movi.n a2, 0 │ 0x00000073 1df0 retw.n │ 0x00000075 0c02 movi.n a2, 0 │ 0x00000077 1df0 retw.n │ 0x00000079 0c02 movi.n a2, 0 │ 0x0000007b 1df0 retw.n │ 0x0000007d 0c02 movi.n a2, 0 │ 0x0000007f 1df0 retw.n │ 0x00000081 0c02 movi.n a2, 0 │ 0x00000082 021df0 l16ui a0, a13, 0x1e0 │ 0x00000085 0c02 movi.n a2, 0 │ 0x00000087 1df0 retw.n │ 0x00000089 0c02 movi.n a2, 0 │ 0x0000008b 1df0 retw.n │ 0x0000008d 0c12 movi.n a2, 1 └ 0x0000008f 1df0 retw.n
If you struggle a bit to read the listing, you can set asm.describe to
true, to get a handy description, but basically, it boils down to movi,
beq and l8ui (as in load a byte as an unsigned integer), so it's pretty easy.
I annotated the listing by hand using the ; command in visual mode,
and dis to set the immediate as a string to have a better overview:
[0x00000065]> pdf ┌ (fcn) fcn.00000000 145 │ 0x00000000 364100 entry a1, 32 ; subroutine entry, 32 bit stack frame │ 0x00000003 820200 l8ui a8, a2, 0 ; a8 = a2[0] │ 0x00000006 3c19 movi.n a9, '1' ; a9 = '1' │ 0x00000008 979859 bne a8, a9, 0x00000065 ; if a2[0] != a9; goto 0x65 │ 0x0000000b 920206 l8ui a9, a2, 6 ; a9 = a2[6] │ 0x0000000e a2a061 movi a10, 'a' ; a10 = 'a' │ 0x00000011 a79954 bne a9, a10, 0x00000069 ; if a2[6] != 'a'; goto end │ 0x00000014 a20207 l8ui a10, a2, 7 ; a10 = a2[7] │ 0x00000017 3b99 addi.n a9, a9, 3 ; a9 = 'd' │ 0x00000019 979a50 bne a10, a9, 0x0000006d ; if a2[7] != 'c'; goto end │ 0x0000001c a20209 l8ui a10, a2, 9 ; a10 = a2[9] │ 0x0000001f 920205 l8ui a9, a2, 5 ; a9 = a2[5] │ 0x00000022 979a4b bne a10, a9, 0x00000071 ; if a2[9] != a2[5]; goto end │ 0x00000025 2ba8 addi.n a10, a8, 2 ; a10 = '3' │ 0x00000027 a7994a bne a9, a10, 0x00000075 ; if a2[5] != a10; goto end │ 0x0000002a 920202 l8ui a9, a2, 2 ; a9 = a2[2] │ 0x0000002d 3b88 addi.n a8, a8, 3 ; a8 = '4' │ 0x0000002f 879946 bne a9, a8, 0x00000079 ; if a2[2] != '4'; goto end │ 0x00000032 92020a l8ui a9, a2, 10 ; a9 = a2[10] │ 0x00000035 82a078 movi a8, 'x' ; a8 = 'x' │ 0x00000038 879941 bne a9, a8, 0x0000007d ; if a2[10] != 'x'; goto end │ 0x0000003b 920208 l8ui a9, a2, 8 ; a9 = a2[8] │ 0x0000003e 4c88 movi.n a8, 'H' ; a8 = 'H' │ 0x00000040 87993d bne a9, a8, 0x00000081 ; if a2[8] != 'H' │ 0x00000043 820201 l8ui a8, a2, 1 ; a8 = a2[1] │ 0x00000046 4c39 movi.n a9, 'C' ; a9 = 'C' │ 0x00000048 979839 bne a8, a9, 0x00000085 ; if a2[1] 1= 'C'; goto end │ 0x0000004b a20203 l8ui a10, a2, 3 ; a10 = a2[3] │ 0x0000004e 2cd9 movi.n a9, 45 ; a9 = 45 │ 0x00000050 909830 xor a9, a8, a9 ; a9 ^= '67' │ 0x00000053 909074 extui a9, a9, 0, 8 ; a9 = a9[0:8] │ 0x00000056 979a2f bne a10, a9, 0x00000089 ; if a2[3] != 'n'; goto end │ 0x00000059 220204 l8ui a2, a2, 4 ; a2 = a2[4] │ 0x0000005c fb88 addi.n a8, a8, 15 ; a8 = 'R' │ 0x0000005e 87122b beq a2, a8, 0x0000008d ; if a2[4] != 'R'; goto end │ 0x00000061 0c02 movi.n a2, 0 ; return value = 0 │ 0x00000063 1df0 retw.n ; return │ 0x00000065 0c02 movi.n a2, 0 │ 0x00000067 1df0 retw.n │ 0x00000069 0c02 movi.n a2, 0 │ 0x0000006b 1df0 retw.n │ 0x0000006c f00c02 andb b0, b12, b15 │ 0x0000006f 1df0 retw.n │ 0x00000071 0c02 movi.n a2, 0 │ 0x00000073 1df0 retw.n │ 0x00000075 0c02 movi.n a2, 0 │ 0x00000077 1df0 retw.n │ 0x00000079 0c02 movi.n a2, 0 │ 0x0000007b 1df0 retw.n │ 0x0000007d 0c02 movi.n a2, 0 │ 0x0000007f 1df0 retw.n │ 0x00000081 0c02 movi.n a2, 0 │ 0x00000082 021df0 l16ui a0, a13, 0x1e0 │ 0x00000085 0c02 movi.n a2, 0 │ 0x00000087 1df0 retw.n │ 0x00000089 0c02 movi.n a2, 0 │ 0x0000008b 1df0 retw.n │ 0x0000008d 0c12 movi.n a2, 1 ; return value = 1 └ 0x0000008f 1df0 retw.n ; return
Getting the flag is only a matter of validating each condition,
yielding you 1C4nR3adH3x.
> game2 1C4nR3adH3x Here is my password checking function. Have fun ! 3641008202003c19979859920206a2a061a79954a202073b99979a50a20209920205979a4b2ba8a7994a9202023b8887994692020a82a0788799419202084c8887993d8202014c39979839a202032cd9909830909074979a2f220204fb8887122b0c021df00c021df00c021df00c021df00c021df00c021df00c021df00c021df00c021df00c021df00c021df00c121df0 Success : Here is a cheat code 88c909
There are at least 7 other flags to find in the badge, maybe I'll write about them if/when I take the time to get a dump of the firmware.
A laziest way to do it would be to use ESIL, by initializing the ESIL virtual
machine with aeim, running until the first jump with aecu (as in Analysis
with ESIL to Continue Until), and to check the corresponding register value:
jvoisin@kaa 16:48 ~ r2 -a xtensa -b 32 - [0x00000000]> wx 3641008202003c19979859920206a2a061a79954a202073b99979a50a20209920205979a4b2ba8a7994a9202023b8887994692020a82a0788799419202084c8887993d8202014c39979839a202032cd9909830909074979a2f220204fb8887122b0c021df00c021df00c021df00c021df00c021df00c021df00c021df00c021df00c021df00c021df00c021df00c121df0 [0x00000000]> pd 8 0x00000000 364100 entry a1, 32 0x00000003 820200 l8ui a8, a2, 0 0x00000006 3c19 movi.n a9, 49 ┌─< 0x00000008 979859 bne a8, a9, 0x00000065 │ 0x0000000b 920206 l8ui a9, a2, 6 │ 0x0000000e a2a061 movi a10, 97 ┌──< 0x00000011 a79954 bne a9, a10, 0x00000069 ││ 0x00000014 a20207 l8ui a10, a2, 7 [0x00000000]> aeim [0x00000000]> aecu 0x00000008 [0x00000000]> pdi 1 @ 0x00000008 0x00000008 979859 bne a8, a9, 0x00000065 [0x00000000]> ? a9 49 0x31 061 49 0000:0031 49 "1" 00110001 49.0 49.000000f 49.000000 [0x00000000]>
So as expected, the first letter is 1. You can get the other ones by manually
setting registers with aer pc = 0x1337 or aer a9 = 0x1338 if you're too
loafer to start over upon each good letter.