Advertisement
Guest User

HITB2018AMS Badge Challenge Write-Up

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