SHARE
TWEET

HITB2018AMS Badge Challenge Write-Up

a guest Apr 17th, 2018 1,644 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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 :)
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top