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.