Advertisement
Guest User

HITB2018AMS Badge Challenge Write-Up

a guest
Apr 17th, 2018
2,007
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 27.18 KB | None | 0 0
  1. --------------------------------------------------------------------
  2. HITBSecConf Amsterdam 2018 Badge Challenge by Qihoo 360 Unicorn Team
  3. --------------------------------------------------------------------
  4. Write-up by: Nikias Bassen (@pimskeks)
  5. April 17, 2018
  6. --------------------------------------------------------------------
  7.  
  8. As a past speaker of HITB I was lucky to get hold of the cool badge that
  9. the Unicorn Team provided for the speakers and attendees at the HackInTheBox
  10. Amsterdam 2018 event last week:
  11.  
  12. https://conference.hitb.org/hitbsecconf2018ams/commsec-village/#badge
  13.  
  14. As announced on Twitter and mentioned on the above page the badge has a hidden
  15. challenge which made me curious and which actually was the reason why I wanted
  16. the badge in the first place. However I was told (not by the Unicorn Team!)
  17. that I _might_ have to solder wires to the breakout pins and connect it via
  18. USB or serial to get a link or something that would give me a clue how to
  19. solve the challenge.
  20.  
  21. At the speakers reception I was finally checking out the badge and found a
  22. "Secret Menu" entry that is asking for a code. Using the Up, Down, Left,
  23. Right, OK, and ESC buttons on the badge it would allow to enter the characters
  24. 'U', 'D', 'L', 'R', 'O', and 'C'. After six characters I was greeted with
  25. "ACCESS DENIED!!! :(". A quick calculation shows that this would be a total of
  26. 6^6 = 46656 combinations, way to much to try them all ;)
  27. I didn't have soldering equipment with me so I wasn't really thinking I could
  28. solve the challenge easily.
  29.  
  30. However since I wasn't following @sgniwx (how could I NOT follow him actually?)
  31. I only realized later that night that they published the source code + binary
  32. on GitHub earlier that day:
  33.  
  34. ---------------------------------------------------------------------------
  35. Source code, binary and the schematic for #HITB2018AMS badge is available
  36. now. Follow @sgniwx @WhiteA10n3 @klks84 and look for us in the conference.
  37. Hint: crackme challenge is deep in the link.
  38. https://github.com/klks/hitb2018ams_badge
  39. ---------------------------------------------------------------------------
  40. (https://twitter.com/sgniwx/status/984106705037471744)
  41.  
  42. So it looked like that I wouldn't have to solder after all (which I totally
  43. wouldn't mind doing - given the right equipment)!
  44.  
  45. Anyway, I checked the source code and I found this:
  46. https://github.com/klks/hitb2018ams_badge/blob/master/src/hitb_secret.h#L58
  47. Which compares the input to "OOOOOO". Of course I knew this wouldn't be the
  48. code since that would be too easy, also the next line saying
  49. 'display_qrcode("/*REDACTED*/");'
  50. was pretty obvious this is not exactly the same code that runs the badge.
  51.  
  52. But wait, they released the BINARY too which can be found here:
  53. https://github.com/klks/hitb2018ams_badge/blob/master/hex/HITB.ino.bin
  54. When searching for "ACCESS DENIED" I found a string "LRDROC" just a few bytes
  55. before, at offset 0xC5FB, which immediately looked like that this would be
  56. the code, and also a short link is right behind it: goo.gl/PhVrhv
  57. (offset 0xC606). I tried the code on the badge and instead of
  58. "ACCESS DENIED!!! :(" it displayed a QR code which just represents the short
  59. link (yes, I checked it with my iPhone camera).
  60.  
  61. Following the short link you will be redirected to:
  62. http://hardware.reverser.io/hitb2018ams/challange/
  63. Which has some really clear instructions:
  64.  
  65. ------------
  66. [binary](http://hardware.reverser.io/hitb2018ams/challange/secretbox)
  67.  
  68. 1. Download and reverse "secretbox" binary
  69. 2. ssh secretbox@secretbox.l4w.pw
  70. 3. password = secretbox
  71. 4. Look for "xwings" and show me your PoW.
  72.  
  73. - Binary by l4w @ vnsec
  74. - xwings @ HITB Core Crew / Unicorn Core Team / vnsec
  75.  
  76. ------------
  77.  
  78. So I downloaded the binary and opened it in IDA Pro. It is a 32-bit Linux binary.
  79. First I was a bit confused about all the 'extra' code but then quickly realized
  80. it was because of the use of ncurses library.
  81. Running it will display a "Token" which is random, and a keypad that you can
  82. navigate with the arrow keys and select with the space key:
  83.  
  84. SAFEBOX KEYPAD
  85. Token: EA7D
  86. ┌───────────────────┐
  87. │ │
  88. │ │
  89. │ [0] [1] [2] [3] │
  90. │ [4] [5] [6] [7] │
  91. │ [8] [9] [A] [B] │
  92. │ [C] [D] [E] [F] │
  93. │ │
  94. │ │
  95. └───────────────────┘
  96.  
  97. After entering 6 digits it will 'FAILED :(' if the code wasn't right. If you
  98. try 3 times and still didn't guess the code it will print 'self desctruct'
  99. all over the screen. Just connect to the server and see what happens.
  100.  
  101. So, obviously the code depends on the random token.
  102.  
  103. Looking at the main function, first thing it does is chdir to
  104. /home/secretbox.
  105. This will be used later. Right after that, there is a call to a sub function:
  106.  
  107. .text:080490EF call sub_8049000
  108.  
  109. This function opens /dev/urandom, reads 2 bytes from it, and snprintf()s that
  110. to a global buffer with format %04X. I renamed the function to
  111. 'get_random_token' and the buffer to TOKEN_BUF.
  112.  
  113. Back in main, the ncurses screen is initialized using initscr, start_color,
  114. init_pair, newwin, etc. You can also see the code that is writing the token
  115. onto the screen, using the global TOKEN_BUF I mentioned above:
  116.  
  117. .text:080492DA mov ecx, 1
  118. .text:080492DF mov edx, 5
  119. .text:080492E4 lea esi, aTokenS ; "Token: %s"
  120. .text:080492EA lea edi, TOKEN_BUF
  121. .text:080492F0 mov dword ptr [esp], 1 ; int
  122. .text:080492F7 mov dword ptr [esp+4], 5 ; int
  123. .text:080492FF mov [esp+8], esi ; char *
  124. .text:08049303 mov [esp+0Ch], edi
  125. .text:08049307 mov [ebp+var_B0], eax
  126. .text:0804930D mov [ebp+var_B4], ecx
  127. .text:08049313 mov [ebp+var_B8], edx
  128. .text:08049319 call _mvprintw
  129.  
  130. Looking further, there is another call to an unknown sub function:
  131.  
  132. .text:080493A0 call sub_8049B50
  133.  
  134. It does some ncurses stuff that draws a border, prints a string with 6 chars
  135. (using mvwprintw with format %06s) from another global buffer, and then it
  136. loops 4 times, sequentially reading 4 chars from a global wide char string
  137. (0123456789ABCDEF) and printing it with format [%s] to different positions
  138. for all the 16 characters. This is the keypad drawing function obviously.
  139. I renamed it to draw_keypad. This function has one interesting piece here,
  140. since it has a buffer that is used to print whatever we entered so far onto
  141. the screen. This function is also redrawing the keypad because it highlights
  142. whatever 'key' we have selected and thus it needs to redraw every time we
  143. move the cursor. That global buffer, I named it ENTERED_WCHAR_BUF. I named
  144. the other global wide char string 'keypad_wchars'.
  145. Going from there I checked the XREFs to see where the code writes to the
  146. buffer, and found this:
  147.  
  148. .text:08049623 mov eax, [ebp+var_14]
  149. .text:08049626 mov [ebp+var_18], eax
  150. .text:08049629 mov eax, [ebp+var_18]
  151. .text:0804962C mov eax, dword_804C0D0[eax*4]
  152. .text:08049633 mov cl, [eax]
  153. .text:08049635 mov eax, [ebp+var_20]
  154. .text:08049638 mov ds:dword_804C130[eax], cl
  155. .text:0804963F mov eax, [ebp+var_18]
  156. .text:08049642 mov eax, keypad_wchars[eax*4]
  157. .text:08049649 mov cl, [eax]
  158. .text:0804964B mov eax, [ebp+var_20]
  159. .text:0804964E mov ds:ENTERED_WCHAR_BUF[eax], cl
  160.  
  161. Now what are those ebp+var_14 and ebp+var_20 ? If you browse through the code
  162. you easily figure out that ebp+var_20 is initially 0 and increases with every
  163. character that is selected through the keypad. It is the offset in the buffer.
  164. For ebp+var_14 it is the index of the keypad number selected (0..15).
  165. Now it reads something from address dword_804C0D0 + keypad_index * 4, so what
  166. is dword_804C0D0? If you check out its location you realize it is has some
  167. byte_* addresses there, each one of them pointing to the bytes (not chars!)
  168. 0, 1, 2, through 0xF. Let's rename it to 'array_00_0F'. Now it reads a byte
  169. from that array and stores that into another global buffer dword_804C130, that
  170. we rename to ENTERED_BUF, because that's what gets entered effectively.
  171. As for ENTERED_WCHAR_BUF, it uses the same keypad index ebp+var_14
  172. (and ebp+var_18) to get the wide char from the 'keypad_wchars' we already
  173. figured out in the draw_keypad function.
  174. So, we have one buffer that stores the byte values selected through the keypad
  175. (ENTERED_BUF) and one buffer that stores the printable chars 0-9A-F
  176. (ENTERED_WCHAR_BUF).
  177.  
  178. If you check the code right after the previous snippet, we see this:
  179.  
  180. .text:08049655 mov eax, [ebp+var_20]
  181. .text:08049658 add eax, 1
  182. .text:0804965B mov [ebp+var_20], eax
  183. .text:0804965E cmp [ebp+var_20], 6
  184. .text:08049662 jnz loc_80498B7
  185. .text:08049668 lea eax, ENTERED_BUF
  186. .text:0804966E lea ecx, TOKEN_BUF
  187. .text:08049674 mov [esp], eax ; int
  188. .text:08049677 mov [esp+4], ecx ; nptr
  189. .text:0804967B call sub_8048E80
  190. .text:08049680 cmp eax, 0
  191. .text:08049683 jz loc_80497FC
  192.  
  193. After checking if the buffer index has reached 6 a sub function is called with
  194. the token buffer (TOKEN_BUF) and the buffer with the selected values
  195. (ENTERED_BUF). This pretty much looks like the verification function! So let's
  196. have a look inside:
  197.  
  198. .text:08048E89 mov eax, [ebp+token]
  199. .text:08048E8C mov ecx, [ebp+entered]
  200. .text:08048E8F xor edx, edx
  201. .text:08048E91 mov esi, 10h
  202. .text:08048E96 mov edi, [ebp+token]
  203. .text:08048E99 mov [esp], edi ; nptr
  204. .text:08048E9C mov dword ptr [esp+4], 0 ; endptr
  205. .text:08048EA4 mov dword ptr [esp+8], 10h ; base
  206. .text:08048EAC mov [ebp+var_20], eax
  207. .text:08048EAF mov [ebp+var_24], ecx
  208. .text:08048EB2 mov [ebp+var_28], edx
  209. .text:08048EB5 mov [ebp+var_2C], esi
  210. .text:08048EB8 call _strtoul
  211. .text:08048EBD mov [ebp+var_10], eax
  212. .text:08048EC0 imul eax, [ebp+var_10], 1337h
  213. .text:08048EC7 mov [esp], eax ; seed
  214. .text:08048ECA call _srand
  215.  
  216. First thing it does is getting the numerical value of the TOKEN_BUF string
  217. that is initialized as explained above using strtoul(). Then it multiplies
  218. that value with 0x1337 and calls srand() to initialize the pseudo-random
  219. number generator.
  220. Following is what must be the verification of every single digit entered.
  221. It looks like this:
  222.  
  223. .text:08048ECF mov ecx, [ebp+entered]
  224. .text:08048ED2 movsx ecx, byte ptr [ecx]
  225. .text:08048ED5 mov [esp], ecx
  226. .text:08048ED8 mov [ebp+var_30], eax
  227. .text:08048EDB call sub_8048AE0
  228. .text:08048EE0 mov bx, ax
  229. .text:08048EE3 mov [ebp+var_12], bx
  230. .text:08048EE7 mov eax, [ebp+entered]
  231. .text:08048EEA movsx eax, byte ptr [eax+1]
  232. .text:08048EEE mov [esp], eax
  233. .text:08048EF1 call sub_8048B30
  234. .text:08048EF6 mov bx, ax
  235. .text:08048EF9 mov [ebp+var_14], bx
  236. .text:08048EFD mov eax, [ebp+entered]
  237. .text:08048F00 movsx eax, byte ptr [eax+2]
  238. .text:08048F04 mov [esp], eax
  239. .text:08048F07 call sub_8048B80
  240. .text:08048F0C mov bx, ax
  241. .text:08048F0F mov [ebp+var_16], bx
  242. .text:08048F13 mov eax, [ebp+entered]
  243. .text:08048F16 movsx eax, byte ptr [eax+3]
  244. .text:08048F1A mov [esp], eax
  245. .text:08048F1D call sub_8048BE0
  246. .text:08048F22 mov bx, ax
  247. .text:08048F25 mov [ebp+var_18], bx
  248. .text:08048F29 mov eax, [ebp+entered]
  249. .text:08048F2C movsx eax, byte ptr [eax+4]
  250. .text:08048F30 mov [esp], eax
  251. .text:08048F33 call sub_8048C40
  252. .text:08048F38 mov bx, ax
  253. .text:08048F3B mov [ebp+var_1A], bx
  254. .text:08048F3F mov eax, [ebp+entered]
  255. .text:08048F42 movsx eax, byte ptr [eax+5]
  256. .text:08048F46 mov [esp], eax
  257. .text:08048F49 call sub_8048CE0
  258. .text:08048F4E mov ecx, 0Ah
  259. .text:08048F53 mov edx, 1Eh
  260. .text:08048F58 lea esi, aUUUUUU ; "%u %u %u %u %u %u"
  261. .text:08048F5E mov bx, ax
  262. .text:08048F61 mov [ebp+var_1C], bx
  263.  
  264. The pattern is pretty clear: Take a byte value from ENTERED_BUF and pass it
  265. to the distinctive sub function. The result is then stored in a corresponding
  266. local variable (var_12, var_14, var_16, var_18, var_1A, and var_1C).
  267. You can see this happening for each of the 6 digits entered. Let's rename the
  268. sub functions to calc_char_0 .. calc_char_5 so it looks like this:
  269.  
  270. .text:08048ECF mov ecx, [ebp+entered]
  271. .text:08048ED2 movsx ecx, byte ptr [ecx]
  272. .text:08048ED5 mov [esp], ecx
  273. .text:08048ED8 mov [ebp+var_30], eax
  274. .text:08048EDB call calc_char_0
  275. .text:08048EE0 mov bx, ax
  276. .text:08048EE3 mov [ebp+var_12], bx
  277. .text:08048EE7 mov eax, [ebp+entered]
  278. .text:08048EEA movsx eax, byte ptr [eax+1]
  279. .text:08048EEE mov [esp], eax
  280. .text:08048EF1 call calc_char_1
  281. .text:08048EF6 mov bx, ax
  282. .text:08048EF9 mov [ebp+var_14], bx
  283. .text:08048EFD mov eax, [ebp+entered]
  284. .text:08048F00 movsx eax, byte ptr [eax+2]
  285. .text:08048F04 mov [esp], eax
  286. .text:08048F07 call calc_char_2
  287. .text:08048F0C mov bx, ax
  288. .text:08048F0F mov [ebp+var_16], bx
  289. .text:08048F13 mov eax, [ebp+entered]
  290. .text:08048F16 movsx eax, byte ptr [eax+3]
  291. .text:08048F1A mov [esp], eax
  292. .text:08048F1D call calc_char_3
  293. .text:08048F22 mov bx, ax
  294. .text:08048F25 mov [ebp+var_18], bx
  295. .text:08048F29 mov eax, [ebp+entered]
  296. .text:08048F2C movsx eax, byte ptr [eax+4]
  297. .text:08048F30 mov [esp], eax
  298. .text:08048F33 call calc_char_4
  299. .text:08048F38 mov bx, ax
  300. .text:08048F3B mov [ebp+var_1A], bx
  301. .text:08048F3F mov eax, [ebp+entered]
  302. .text:08048F42 movsx eax, byte ptr [eax+5]
  303. .text:08048F46 mov [esp], eax
  304. .text:08048F49 call calc_char_5
  305. .text:08048F4E mov ecx, 0Ah
  306. .text:08048F53 mov edx, 1Eh
  307. .text:08048F58 lea esi, aUUUUUU ; "%u %u %u %u %u %u"
  308. .text:08048F5E mov bx, ax
  309. .text:08048F61 mov [ebp+var_1C], bx
  310.  
  311. Also you can see "%u %u %u %u %u %u" format string and if you continue looking
  312. you will find out that it will print the result of every function on the
  313. screen with mvprintw.
  314.  
  315. After that it will verify the result for every digit, and logically AND the
  316. resulting values here:
  317.  
  318. .text:08048FC5 movzx ecx, [ebp+var_12]
  319. .text:08048FC9 movzx edx, [ebp+var_14]
  320. .text:08048FCD and ecx, edx
  321. .text:08048FCF movzx edx, [ebp+var_16]
  322. .text:08048FD3 and ecx, edx
  323. .text:08048FD5 movzx edx, [ebp+var_18]
  324. .text:08048FD9 and ecx, edx
  325. .text:08048FDB movzx edx, [ebp+var_1A]
  326. .text:08048FDF and ecx, edx
  327. .text:08048FE1 movzx edx, [ebp+var_1C]
  328. .text:08048FE5 and ecx, edx
  329. .text:08048FE7 mov [ebp+var_48], eax
  330. .text:08048FEA mov eax, ecx
  331.  
  332. So only if all of the sub functions returns TRUE this function (verify_code)
  333. will return TRUE, otherwise FALSE.
  334.  
  335. Now let's find out how the code is calculated.
  336.  
  337. Looking into first function it looks really simple. It calls rand() and does
  338. modulo 16 with the result, giving a number 0..15. That result is stored in a
  339. global variable, let's call it char_0_res. It is then compared to the value
  340. that was entered by the user.
  341. So calc_char_0 returns TRUE or FALSE, based on the input.
  342.  
  343. In pseudo code it looks like this:
  344.  
  345. BOOL calc_char_0(char a1)
  346. {
  347. char_0_res = rand() % 16;
  348. return a1 == char_0_res;
  349. }
  350.  
  351. calc_char_1 is doing a little more. It also calls rand() and does modulo 16,
  352. and stores the result in another global that we call char_1_res. Now the
  353. result for the FIRST digit is multiplied by 8 and then modulo'ed with the
  354. char_1_res, finally comparing it to the input:
  355.  
  356. BOOL calc_char_1(char a1)
  357. {
  358. char_1_res = rand() % 16;
  359. return (8 * char_0_res % char_1_res) == a1;
  360. }
  361.  
  362. Next up is calc_char_2. After calculating the rand() modulo 16 again and
  363. storing it in another global char_2_res, it will add up that value with the
  364. result of the first and second value, and ANDs it with 15, then compares it
  365. with the input:
  366.  
  367. BOOL calc_char_2(char a1)
  368. {
  369. char_2_res = rand() % 16;
  370. return ((char_2_res + char_1_res + char_0_res) & 0xF) == a1;
  371. }
  372.  
  373. Now we have calc_char_3. This one is a little different. It will again do the
  374. usual rand() % 16 and store it it char_3_res.
  375. But then it subtracts the result of the 3rd digit (char_2_res) from the result
  376. of the 1st digit (char_0_res) and adds the result of ANOTHER call to rand() to
  377. it. Then it ANDs it with 15 and compares it with the input value:
  378.  
  379. BOOL calc_char_3(char a1)
  380. {
  381. char v1;
  382.  
  383. char_3_res = rand() % 16;
  384. v1 = char_0_res - char_2_res;
  385. return ((rand() + v1) & 0xF) == a1;
  386. }
  387.  
  388. Then we have calc_char_4. This one again is different. We have the usual
  389. char_4_res = rand() % 16. Then it will do a loop with char_4_res iterations,
  390. and in very iteration it calls rand() (so minimum of 0, maximum of 15
  391. iterations). After the loop it will do rand() % 4 giving a result that is then
  392. used in another calculation with yet another rand(). Just look at the
  393. pseudo-code, which will be easier to understand than words. Of course the
  394. final result is compared to the input value:
  395.  
  396. BOOL calc_char_4(char a1)
  397. {
  398. int v1;
  399. unsigned int i;
  400.  
  401. char_4_res = rand() % 16;
  402. for ( i = 0; i < char_4_res; ++i )
  403. rand();
  404. v1 = rand() % 4;
  405. return (rand() % 5 * v1 % 16) == a1;
  406. }
  407.  
  408. Finally calc_char_5. This function is first doing the usual
  409. char_5_res = rand() % 16, and also, like calc_char_4, doing a loop with rand()
  410. calls based on that result as the number of iterations. After that, it does a
  411. calculation with all the previous results (calc_0_res through calc_5_res) and
  412. comparing it with the value passed as argument.
  413. Again, easier to just understand the pseudo-code:
  414.  
  415. BOOL calc_char_5(char a1)
  416. {
  417. unsigned int v1;
  418. unsigned int i;
  419.  
  420. char_5_res = rand() % 16;
  421. for ( i = 0; i < char_5_res; ++i )
  422. rand();
  423. v1 = char_3_res * ((char_0_res << char_1_res) / (unsigned int)char_2_res);
  424. return (((unsigned char)(rand() % 5) * (uint8_t)v1 - char_4_res % 7 / (char_5_res & 0xF)) & 0xF) == a1;
  425. }
  426.  
  427. So now that we know how it verifies the code, we can easily write a keygen
  428. for it. Basically we just take all the 6 functions and remove the comparison
  429. with the input value. It will leave us with these 6 functions:
  430.  
  431. uint8_t char_0_res = 0;
  432. uint8_t char_1_res = 0;
  433. uint8_t char_2_res = 0;
  434. uint8_t char_3_res = 0;
  435. uint8_t char_4_res = 0;
  436. uint8_t char_5_res = 0;
  437.  
  438. static uint8_t calc_char_0()
  439. {
  440. char_0_res = rand() % 16;
  441. return char_0_res;
  442. }
  443.  
  444. static uint8_t calc_char_1()
  445. {
  446. char_1_res = rand() % 16;
  447. return 8 * char_0_res % char_1_res;
  448. }
  449.  
  450. static uint8_t calc_char_2()
  451. {
  452. char_2_res = rand() % 16;
  453. return (char_2_res + char_1_res + char_0_res) & 0xF;
  454. }
  455.  
  456. static uint8_t calc_char_3()
  457. {
  458. char v1;
  459. char_3_res = rand() % 16;
  460. v1 = char_0_res - char_2_res;
  461. return ((unsigned char)rand() + v1) & 0xF;
  462. }
  463.  
  464. static uint8_t calc_char_4()
  465. {
  466. unsigned int v1;
  467. unsigned int i;
  468.  
  469. char_4_res = rand() % 16;
  470. for (i = 0; i < char_4_res; ++i)
  471. rand();
  472. v1 = rand() % 4;
  473. return rand() % 5 * v1 % 16;
  474. }
  475.  
  476. static uint8_t calc_char_5()
  477. {
  478. unsigned int v1;
  479. unsigned int i;
  480.  
  481. char_5_res = rand() % 16;
  482. for (i = 0; i < char_5_res; ++i)
  483. rand();
  484. v1 = char_3_res * ((char_0_res << char_1_res) / (unsigned int)char_2_res);
  485. return ((unsigned char)(rand() % 5) * (uint8_t)v1 - char_4_res % 7 / (char_5_res & 0xF)) & 0xF;
  486. }
  487.  
  488.  
  489. Now, the only thing we have to do is allowing to input a token value so we
  490. can initialize the pseudo random number generator.
  491. I ended up with this main function:
  492.  
  493.  
  494. int main(int argc, char **argv)
  495. {
  496. if (argc < 2) {
  497. char *prog = strrchr(argv[0], '/');
  498. printf("Usage: %s TOKEN\n", (prog) ? prog+1 : argv[0]);
  499. return -1;
  500. }
  501.  
  502. uint32_t token = strtoul(argv[1], NULL, 16);
  503.  
  504. printf("TOKEN IS 0x%x\n", token);
  505.  
  506. srand(token * 0x1337);
  507.  
  508. uint8_t c0 = calc_char_0();
  509. uint8_t c1 = calc_char_1();
  510. uint8_t c2 = calc_char_2();
  511. uint8_t c3 = calc_char_3();
  512. uint8_t c4 = calc_char_4();
  513. uint8_t c5 = calc_char_5();
  514.  
  515. printf("CODE: %x %x %x %x %x %x\n", c0, c1, c2, c3, c4, c5);
  516.  
  517. return 0;
  518. }
  519.  
  520. The full C file can be found here: https://pastebin.com/pGNp3hHY
  521.  
  522. Just run this on your linux box passing the token value (expected to be
  523. hexadecimal), and it will calculate and print the corresponding code.
  524.  
  525. Then you can log in to the secrebox server, pass the token from that
  526. session to the key generator, and enter the correct code. You will get the
  527. flag printed on screen.
  528. But hey. Couldn't we just extract the flag from the binary? No, because it's
  529. not included. So how is the flag retrieved? What happens when you enter the
  530. correct code, actually?
  531. Let's jump back to the main function. If verfiy_code returns TRUE, the jump
  532. after it will not be taken. The code following has a reference to a string
  533. "CORRECT" and also a "secret.txt", followed by a call to another sub function:
  534.  
  535. .text:0804967B call verify_code
  536. .text:08049680 cmp eax, 0
  537. .text:08049683 jz loc_80497FC
  538. .text:08049689 mov eax, 200h
  539. .text:0804968E xor ecx, ecx
  540. .text:08049690 mov edx, ds:stdscr
  541. .text:08049696 mov [esp], edx ; WINDOW *
  542. .text:08049699 mov dword ptr [esp+4], 200h ; attr_t
  543. .text:080496A1 mov dword ptr [esp+8], 0 ; void *
  544. .text:080496A9 mov [ebp+var_11C], eax
  545. .text:080496AF mov [ebp+var_120], ecx
  546. .text:080496B5 call _wattr_on
  547. .text:080496BA mov ecx, 3
  548. .text:080496BF mov edx, 28h ; '('
  549. .text:080496C4 lea esi, aCorrect ; "CORRECT"
  550. .text:080496CA mov dword ptr [esp], 3 ; int
  551. .text:080496D1 mov dword ptr [esp+4], 28h ; '(' ; int
  552. .text:080496D9 mov [esp+8], esi ; char *
  553. .text:080496DD mov [ebp+var_124], eax
  554. .text:080496E3 mov [ebp+var_128], ecx
  555. .text:080496E9 mov [ebp+var_12C], edx
  556. .text:080496EF call _mvprintw
  557. .text:080496F4 lea ecx, aSecretTxt ; "secret.txt"
  558. .text:080496FA mov [esp], ecx ; filename
  559. .text:080496FD mov [ebp+var_130], eax
  560. .text:08049703 call sub_8048DA0
  561.  
  562. The purpose of that function is easily identified. It opens a file, gets its
  563. size, calls 'malloc' with that size, and reads the contents of that file into
  564. that buffer. That's it. After returning from that function, you can see it
  565. will do some ncurses printing that will eventually print whatever is in that
  566. secret.txt file:
  567.  
  568. .text:08049708 mov ecx, 5
  569. .text:0804970D mov edx, 1Eh
  570. .text:08049712 lea esi, asc_8049F7C ; "+---------------------------------+"
  571. .text:08049718 mov [ebp+var_28], eax
  572. .text:0804971B mov dword ptr [esp], 5 ; int
  573. .text:08049722 mov dword ptr [esp+4], 1Eh ; int
  574. .text:0804972A mov [esp+8], esi ; char *
  575. .text:0804972E mov [ebp+var_134], edx
  576. .text:08049734 mov [ebp+var_138], ecx
  577. .text:0804973A call _mvprintw
  578. .text:0804973F mov ecx, 6
  579. .text:08049744 mov edx, 1Eh
  580. .text:08049749 lea esi, a30s ; "|%30s |"
  581. .text:0804974F mov edi, [ebp+var_28]
  582. .text:08049752 mov dword ptr [esp], 6 ; int
  583. .text:08049759 mov dword ptr [esp+4], 1Eh ; int
  584. .text:08049761 mov [esp+8], esi ; char *
  585. .text:08049765 mov [esp+0Ch], edi
  586. .text:08049769 mov [ebp+var_13C], eax
  587. .text:0804976F mov [ebp+var_140], ecx
  588. .text:08049775 mov [ebp+var_144], edx
  589. .text:0804977B call _mvprintw
  590. .text:08049780 mov ecx, 7
  591. .text:08049785 mov edx, 1Eh
  592. .text:0804978A lea esi, asc_8049F7C ; "+---------------------------------+"
  593. .text:08049790 mov dword ptr [esp], 7 ; int
  594. .text:08049797 mov dword ptr [esp+4], 1Eh ; int
  595. .text:0804979F mov [esp+8], esi ; char *
  596. .text:080497A3 mov [ebp+var_148], eax
  597. .text:080497A9 mov [ebp+var_14C], ecx
  598. .text:080497AF mov [ebp+var_150], edx
  599. .text:080497B5 call _mvprintw
  600.  
  601. So if you enter the correct code, the output will look like this:
  602.  
  603. SAFEBOX KEYPAD
  604. Token: A1F0
  605. ┌───────────────────┐
  606. │ 002530 │ CORRECT
  607. │ │
  608. │ [0] [1] [2] [3] │ +---------------------------------+
  609. │ [4] [5] [6] [7] │ | flag{welc0me_to_H1T13}
  610. │ [8] [9] [A] [B] │ +---------------------------------+
  611. │ [C] [D] [E] [F] │
  612. │ │
  613. │ │ 1 1 1 1 1 1
  614. └───────────────────┘
  615.  
  616. You've pressed [0]
  617.  
  618.  
  619. That's it! Now you know the flag :)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement