Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --------------------------------------------------------------------
- HITBSecConf Amsterdam 2018 Badge Challenge by Qihoo 360 Unicorn Team
- --------------------------------------------------------------------
- Write-up by: Nikias Bassen (@pimskeks)
- April 17, 2018
- --------------------------------------------------------------------
- As a past speaker of HITB I was lucky to get hold of the cool badge that
- the Unicorn Team provided for the speakers and attendees at the HackInTheBox
- Amsterdam 2018 event last week:
- https://conference.hitb.org/hitbsecconf2018ams/commsec-village/#badge
- As announced on Twitter and mentioned on the above page the badge has a hidden
- challenge which made me curious and which actually was the reason why I wanted
- the badge in the first place. However I was told (not by the Unicorn Team!)
- that I _might_ have to solder wires to the breakout pins and connect it via
- USB or serial to get a link or something that would give me a clue how to
- solve the challenge.
- At the speakers reception I was finally checking out the badge and found a
- "Secret Menu" entry that is asking for a code. Using the Up, Down, Left,
- Right, OK, and ESC buttons on the badge it would allow to enter the characters
- 'U', 'D', 'L', 'R', 'O', and 'C'. After six characters I was greeted with
- "ACCESS DENIED!!! :(". A quick calculation shows that this would be a total of
- 6^6 = 46656 combinations, way to much to try them all ;)
- I didn't have soldering equipment with me so I wasn't really thinking I could
- solve the challenge easily.
- However since I wasn't following @sgniwx (how could I NOT follow him actually?)
- I only realized later that night that they published the source code + binary
- on GitHub earlier that day:
- ---------------------------------------------------------------------------
- Source code, binary and the schematic for #HITB2018AMS badge is available
- now. Follow @sgniwx @WhiteA10n3 @klks84 and look for us in the conference.
- Hint: crackme challenge is deep in the link.
- https://github.com/klks/hitb2018ams_badge
- ---------------------------------------------------------------------------
- (https://twitter.com/sgniwx/status/984106705037471744)
- So it looked like that I wouldn't have to solder after all (which I totally
- wouldn't mind doing - given the right equipment)!
- Anyway, I checked the source code and I found this:
- https://github.com/klks/hitb2018ams_badge/blob/master/src/hitb_secret.h#L58
- Which compares the input to "OOOOOO". Of course I knew this wouldn't be the
- code since that would be too easy, also the next line saying
- 'display_qrcode("/*REDACTED*/");'
- was pretty obvious this is not exactly the same code that runs the badge.
- But wait, they released the BINARY too which can be found here:
- https://github.com/klks/hitb2018ams_badge/blob/master/hex/HITB.ino.bin
- When searching for "ACCESS DENIED" I found a string "LRDROC" just a few bytes
- before, at offset 0xC5FB, which immediately looked like that this would be
- the code, and also a short link is right behind it: goo.gl/PhVrhv
- (offset 0xC606). I tried the code on the badge and instead of
- "ACCESS DENIED!!! :(" it displayed a QR code which just represents the short
- link (yes, I checked it with my iPhone camera).
- Following the short link you will be redirected to:
- http://hardware.reverser.io/hitb2018ams/challange/
- Which has some really clear instructions:
- ------------
- [binary](http://hardware.reverser.io/hitb2018ams/challange/secretbox)
- 1. Download and reverse "secretbox" binary
- 2. ssh secretbox@secretbox.l4w.pw
- 3. password = secretbox
- 4. Look for "xwings" and show me your PoW.
- - Binary by l4w @ vnsec
- - xwings @ HITB Core Crew / Unicorn Core Team / vnsec
- ------------
- So I downloaded the binary and opened it in IDA Pro. It is a 32-bit Linux binary.
- First I was a bit confused about all the 'extra' code but then quickly realized
- it was because of the use of ncurses library.
- Running it will display a "Token" which is random, and a keypad that you can
- navigate with the arrow keys and select with the space key:
- SAFEBOX KEYPAD
- Token: EA7D
- ┌───────────────────┐
- │ │
- │ │
- │ [0] [1] [2] [3] │
- │ [4] [5] [6] [7] │
- │ [8] [9] [A] [B] │
- │ [C] [D] [E] [F] │
- │ │
- │ │
- └───────────────────┘
- After entering 6 digits it will 'FAILED :(' if the code wasn't right. If you
- try 3 times and still didn't guess the code it will print 'self desctruct'
- all over the screen. Just connect to the server and see what happens.
- So, obviously the code depends on the random token.
- Looking at the main function, first thing it does is chdir to
- /home/secretbox.
- This will be used later. Right after that, there is a call to a sub function:
- .text:080490EF call sub_8049000
- This function opens /dev/urandom, reads 2 bytes from it, and snprintf()s that
- to a global buffer with format %04X. I renamed the function to
- 'get_random_token' and the buffer to TOKEN_BUF.
- Back in main, the ncurses screen is initialized using initscr, start_color,
- init_pair, newwin, etc. You can also see the code that is writing the token
- onto the screen, using the global TOKEN_BUF I mentioned above:
- .text:080492DA mov ecx, 1
- .text:080492DF mov edx, 5
- .text:080492E4 lea esi, aTokenS ; "Token: %s"
- .text:080492EA lea edi, TOKEN_BUF
- .text:080492F0 mov dword ptr [esp], 1 ; int
- .text:080492F7 mov dword ptr [esp+4], 5 ; int
- .text:080492FF mov [esp+8], esi ; char *
- .text:08049303 mov [esp+0Ch], edi
- .text:08049307 mov [ebp+var_B0], eax
- .text:0804930D mov [ebp+var_B4], ecx
- .text:08049313 mov [ebp+var_B8], edx
- .text:08049319 call _mvprintw
- Looking further, there is another call to an unknown sub function:
- .text:080493A0 call sub_8049B50
- It does some ncurses stuff that draws a border, prints a string with 6 chars
- (using mvwprintw with format %06s) from another global buffer, and then it
- loops 4 times, sequentially reading 4 chars from a global wide char string
- (0123456789ABCDEF) and printing it with format [%s] to different positions
- for all the 16 characters. This is the keypad drawing function obviously.
- I renamed it to draw_keypad. This function has one interesting piece here,
- since it has a buffer that is used to print whatever we entered so far onto
- the screen. This function is also redrawing the keypad because it highlights
- whatever 'key' we have selected and thus it needs to redraw every time we
- move the cursor. That global buffer, I named it ENTERED_WCHAR_BUF. I named
- the other global wide char string 'keypad_wchars'.
- Going from there I checked the XREFs to see where the code writes to the
- buffer, and found this:
- .text:08049623 mov eax, [ebp+var_14]
- .text:08049626 mov [ebp+var_18], eax
- .text:08049629 mov eax, [ebp+var_18]
- .text:0804962C mov eax, dword_804C0D0[eax*4]
- .text:08049633 mov cl, [eax]
- .text:08049635 mov eax, [ebp+var_20]
- .text:08049638 mov ds:dword_804C130[eax], cl
- .text:0804963F mov eax, [ebp+var_18]
- .text:08049642 mov eax, keypad_wchars[eax*4]
- .text:08049649 mov cl, [eax]
- .text:0804964B mov eax, [ebp+var_20]
- .text:0804964E mov ds:ENTERED_WCHAR_BUF[eax], cl
- Now what are those ebp+var_14 and ebp+var_20 ? If you browse through the code
- you easily figure out that ebp+var_20 is initially 0 and increases with every
- character that is selected through the keypad. It is the offset in the buffer.
- For ebp+var_14 it is the index of the keypad number selected (0..15).
- Now it reads something from address dword_804C0D0 + keypad_index * 4, so what
- is dword_804C0D0? If you check out its location you realize it is has some
- byte_* addresses there, each one of them pointing to the bytes (not chars!)
- 0, 1, 2, through 0xF. Let's rename it to 'array_00_0F'. Now it reads a byte
- from that array and stores that into another global buffer dword_804C130, that
- we rename to ENTERED_BUF, because that's what gets entered effectively.
- As for ENTERED_WCHAR_BUF, it uses the same keypad index ebp+var_14
- (and ebp+var_18) to get the wide char from the 'keypad_wchars' we already
- figured out in the draw_keypad function.
- So, we have one buffer that stores the byte values selected through the keypad
- (ENTERED_BUF) and one buffer that stores the printable chars 0-9A-F
- (ENTERED_WCHAR_BUF).
- If you check the code right after the previous snippet, we see this:
- .text:08049655 mov eax, [ebp+var_20]
- .text:08049658 add eax, 1
- .text:0804965B mov [ebp+var_20], eax
- .text:0804965E cmp [ebp+var_20], 6
- .text:08049662 jnz loc_80498B7
- .text:08049668 lea eax, ENTERED_BUF
- .text:0804966E lea ecx, TOKEN_BUF
- .text:08049674 mov [esp], eax ; int
- .text:08049677 mov [esp+4], ecx ; nptr
- .text:0804967B call sub_8048E80
- .text:08049680 cmp eax, 0
- .text:08049683 jz loc_80497FC
- After checking if the buffer index has reached 6 a sub function is called with
- the token buffer (TOKEN_BUF) and the buffer with the selected values
- (ENTERED_BUF). This pretty much looks like the verification function! So let's
- have a look inside:
- .text:08048E89 mov eax, [ebp+token]
- .text:08048E8C mov ecx, [ebp+entered]
- .text:08048E8F xor edx, edx
- .text:08048E91 mov esi, 10h
- .text:08048E96 mov edi, [ebp+token]
- .text:08048E99 mov [esp], edi ; nptr
- .text:08048E9C mov dword ptr [esp+4], 0 ; endptr
- .text:08048EA4 mov dword ptr [esp+8], 10h ; base
- .text:08048EAC mov [ebp+var_20], eax
- .text:08048EAF mov [ebp+var_24], ecx
- .text:08048EB2 mov [ebp+var_28], edx
- .text:08048EB5 mov [ebp+var_2C], esi
- .text:08048EB8 call _strtoul
- .text:08048EBD mov [ebp+var_10], eax
- .text:08048EC0 imul eax, [ebp+var_10], 1337h
- .text:08048EC7 mov [esp], eax ; seed
- .text:08048ECA call _srand
- First thing it does is getting the numerical value of the TOKEN_BUF string
- that is initialized as explained above using strtoul(). Then it multiplies
- that value with 0x1337 and calls srand() to initialize the pseudo-random
- number generator.
- Following is what must be the verification of every single digit entered.
- It looks like this:
- .text:08048ECF mov ecx, [ebp+entered]
- .text:08048ED2 movsx ecx, byte ptr [ecx]
- .text:08048ED5 mov [esp], ecx
- .text:08048ED8 mov [ebp+var_30], eax
- .text:08048EDB call sub_8048AE0
- .text:08048EE0 mov bx, ax
- .text:08048EE3 mov [ebp+var_12], bx
- .text:08048EE7 mov eax, [ebp+entered]
- .text:08048EEA movsx eax, byte ptr [eax+1]
- .text:08048EEE mov [esp], eax
- .text:08048EF1 call sub_8048B30
- .text:08048EF6 mov bx, ax
- .text:08048EF9 mov [ebp+var_14], bx
- .text:08048EFD mov eax, [ebp+entered]
- .text:08048F00 movsx eax, byte ptr [eax+2]
- .text:08048F04 mov [esp], eax
- .text:08048F07 call sub_8048B80
- .text:08048F0C mov bx, ax
- .text:08048F0F mov [ebp+var_16], bx
- .text:08048F13 mov eax, [ebp+entered]
- .text:08048F16 movsx eax, byte ptr [eax+3]
- .text:08048F1A mov [esp], eax
- .text:08048F1D call sub_8048BE0
- .text:08048F22 mov bx, ax
- .text:08048F25 mov [ebp+var_18], bx
- .text:08048F29 mov eax, [ebp+entered]
- .text:08048F2C movsx eax, byte ptr [eax+4]
- .text:08048F30 mov [esp], eax
- .text:08048F33 call sub_8048C40
- .text:08048F38 mov bx, ax
- .text:08048F3B mov [ebp+var_1A], bx
- .text:08048F3F mov eax, [ebp+entered]
- .text:08048F42 movsx eax, byte ptr [eax+5]
- .text:08048F46 mov [esp], eax
- .text:08048F49 call sub_8048CE0
- .text:08048F4E mov ecx, 0Ah
- .text:08048F53 mov edx, 1Eh
- .text:08048F58 lea esi, aUUUUUU ; "%u %u %u %u %u %u"
- .text:08048F5E mov bx, ax
- .text:08048F61 mov [ebp+var_1C], bx
- The pattern is pretty clear: Take a byte value from ENTERED_BUF and pass it
- to the distinctive sub function. The result is then stored in a corresponding
- local variable (var_12, var_14, var_16, var_18, var_1A, and var_1C).
- You can see this happening for each of the 6 digits entered. Let's rename the
- sub functions to calc_char_0 .. calc_char_5 so it looks like this:
- .text:08048ECF mov ecx, [ebp+entered]
- .text:08048ED2 movsx ecx, byte ptr [ecx]
- .text:08048ED5 mov [esp], ecx
- .text:08048ED8 mov [ebp+var_30], eax
- .text:08048EDB call calc_char_0
- .text:08048EE0 mov bx, ax
- .text:08048EE3 mov [ebp+var_12], bx
- .text:08048EE7 mov eax, [ebp+entered]
- .text:08048EEA movsx eax, byte ptr [eax+1]
- .text:08048EEE mov [esp], eax
- .text:08048EF1 call calc_char_1
- .text:08048EF6 mov bx, ax
- .text:08048EF9 mov [ebp+var_14], bx
- .text:08048EFD mov eax, [ebp+entered]
- .text:08048F00 movsx eax, byte ptr [eax+2]
- .text:08048F04 mov [esp], eax
- .text:08048F07 call calc_char_2
- .text:08048F0C mov bx, ax
- .text:08048F0F mov [ebp+var_16], bx
- .text:08048F13 mov eax, [ebp+entered]
- .text:08048F16 movsx eax, byte ptr [eax+3]
- .text:08048F1A mov [esp], eax
- .text:08048F1D call calc_char_3
- .text:08048F22 mov bx, ax
- .text:08048F25 mov [ebp+var_18], bx
- .text:08048F29 mov eax, [ebp+entered]
- .text:08048F2C movsx eax, byte ptr [eax+4]
- .text:08048F30 mov [esp], eax
- .text:08048F33 call calc_char_4
- .text:08048F38 mov bx, ax
- .text:08048F3B mov [ebp+var_1A], bx
- .text:08048F3F mov eax, [ebp+entered]
- .text:08048F42 movsx eax, byte ptr [eax+5]
- .text:08048F46 mov [esp], eax
- .text:08048F49 call calc_char_5
- .text:08048F4E mov ecx, 0Ah
- .text:08048F53 mov edx, 1Eh
- .text:08048F58 lea esi, aUUUUUU ; "%u %u %u %u %u %u"
- .text:08048F5E mov bx, ax
- .text:08048F61 mov [ebp+var_1C], bx
- Also you can see "%u %u %u %u %u %u" format string and if you continue looking
- you will find out that it will print the result of every function on the
- screen with mvprintw.
- After that it will verify the result for every digit, and logically AND the
- resulting values here:
- .text:08048FC5 movzx ecx, [ebp+var_12]
- .text:08048FC9 movzx edx, [ebp+var_14]
- .text:08048FCD and ecx, edx
- .text:08048FCF movzx edx, [ebp+var_16]
- .text:08048FD3 and ecx, edx
- .text:08048FD5 movzx edx, [ebp+var_18]
- .text:08048FD9 and ecx, edx
- .text:08048FDB movzx edx, [ebp+var_1A]
- .text:08048FDF and ecx, edx
- .text:08048FE1 movzx edx, [ebp+var_1C]
- .text:08048FE5 and ecx, edx
- .text:08048FE7 mov [ebp+var_48], eax
- .text:08048FEA mov eax, ecx
- So only if all of the sub functions returns TRUE this function (verify_code)
- will return TRUE, otherwise FALSE.
- Now let's find out how the code is calculated.
- Looking into first function it looks really simple. It calls rand() and does
- modulo 16 with the result, giving a number 0..15. That result is stored in a
- global variable, let's call it char_0_res. It is then compared to the value
- that was entered by the user.
- So calc_char_0 returns TRUE or FALSE, based on the input.
- In pseudo code it looks like this:
- BOOL calc_char_0(char a1)
- {
- char_0_res = rand() % 16;
- return a1 == char_0_res;
- }
- calc_char_1 is doing a little more. It also calls rand() and does modulo 16,
- and stores the result in another global that we call char_1_res. Now the
- result for the FIRST digit is multiplied by 8 and then modulo'ed with the
- char_1_res, finally comparing it to the input:
- BOOL calc_char_1(char a1)
- {
- char_1_res = rand() % 16;
- return (8 * char_0_res % char_1_res) == a1;
- }
- Next up is calc_char_2. After calculating the rand() modulo 16 again and
- storing it in another global char_2_res, it will add up that value with the
- result of the first and second value, and ANDs it with 15, then compares it
- with the input:
- BOOL calc_char_2(char a1)
- {
- char_2_res = rand() % 16;
- return ((char_2_res + char_1_res + char_0_res) & 0xF) == a1;
- }
- Now we have calc_char_3. This one is a little different. It will again do the
- usual rand() % 16 and store it it char_3_res.
- But then it subtracts the result of the 3rd digit (char_2_res) from the result
- of the 1st digit (char_0_res) and adds the result of ANOTHER call to rand() to
- it. Then it ANDs it with 15 and compares it with the input value:
- BOOL calc_char_3(char a1)
- {
- char v1;
- char_3_res = rand() % 16;
- v1 = char_0_res - char_2_res;
- return ((rand() + v1) & 0xF) == a1;
- }
- Then we have calc_char_4. This one again is different. We have the usual
- char_4_res = rand() % 16. Then it will do a loop with char_4_res iterations,
- and in very iteration it calls rand() (so minimum of 0, maximum of 15
- iterations). After the loop it will do rand() % 4 giving a result that is then
- used in another calculation with yet another rand(). Just look at the
- pseudo-code, which will be easier to understand than words. Of course the
- final result is compared to the input value:
- BOOL calc_char_4(char a1)
- {
- int v1;
- unsigned int i;
- char_4_res = rand() % 16;
- for ( i = 0; i < char_4_res; ++i )
- rand();
- v1 = rand() % 4;
- return (rand() % 5 * v1 % 16) == a1;
- }
- Finally calc_char_5. This function is first doing the usual
- char_5_res = rand() % 16, and also, like calc_char_4, doing a loop with rand()
- calls based on that result as the number of iterations. After that, it does a
- calculation with all the previous results (calc_0_res through calc_5_res) and
- comparing it with the value passed as argument.
- Again, easier to just understand the pseudo-code:
- BOOL calc_char_5(char a1)
- {
- unsigned int v1;
- unsigned int i;
- char_5_res = rand() % 16;
- for ( i = 0; i < char_5_res; ++i )
- rand();
- v1 = char_3_res * ((char_0_res << char_1_res) / (unsigned int)char_2_res);
- return (((unsigned char)(rand() % 5) * (uint8_t)v1 - char_4_res % 7 / (char_5_res & 0xF)) & 0xF) == a1;
- }
- So now that we know how it verifies the code, we can easily write a keygen
- for it. Basically we just take all the 6 functions and remove the comparison
- with the input value. It will leave us with these 6 functions:
- uint8_t char_0_res = 0;
- uint8_t char_1_res = 0;
- uint8_t char_2_res = 0;
- uint8_t char_3_res = 0;
- uint8_t char_4_res = 0;
- uint8_t char_5_res = 0;
- static uint8_t calc_char_0()
- {
- char_0_res = rand() % 16;
- return char_0_res;
- }
- static uint8_t calc_char_1()
- {
- char_1_res = rand() % 16;
- return 8 * char_0_res % char_1_res;
- }
- static uint8_t calc_char_2()
- {
- char_2_res = rand() % 16;
- return (char_2_res + char_1_res + char_0_res) & 0xF;
- }
- static uint8_t calc_char_3()
- {
- char v1;
- char_3_res = rand() % 16;
- v1 = char_0_res - char_2_res;
- return ((unsigned char)rand() + v1) & 0xF;
- }
- static uint8_t calc_char_4()
- {
- unsigned int v1;
- unsigned int i;
- char_4_res = rand() % 16;
- for (i = 0; i < char_4_res; ++i)
- rand();
- v1 = rand() % 4;
- return rand() % 5 * v1 % 16;
- }
- static uint8_t calc_char_5()
- {
- unsigned int v1;
- unsigned int i;
- char_5_res = rand() % 16;
- for (i = 0; i < char_5_res; ++i)
- rand();
- v1 = char_3_res * ((char_0_res << char_1_res) / (unsigned int)char_2_res);
- return ((unsigned char)(rand() % 5) * (uint8_t)v1 - char_4_res % 7 / (char_5_res & 0xF)) & 0xF;
- }
- Now, the only thing we have to do is allowing to input a token value so we
- can initialize the pseudo random number generator.
- I ended up with this main function:
- int main(int argc, char **argv)
- {
- if (argc < 2) {
- char *prog = strrchr(argv[0], '/');
- printf("Usage: %s TOKEN\n", (prog) ? prog+1 : argv[0]);
- return -1;
- }
- uint32_t token = strtoul(argv[1], NULL, 16);
- printf("TOKEN IS 0x%x\n", token);
- srand(token * 0x1337);
- uint8_t c0 = calc_char_0();
- uint8_t c1 = calc_char_1();
- uint8_t c2 = calc_char_2();
- uint8_t c3 = calc_char_3();
- uint8_t c4 = calc_char_4();
- uint8_t c5 = calc_char_5();
- printf("CODE: %x %x %x %x %x %x\n", c0, c1, c2, c3, c4, c5);
- return 0;
- }
- The full C file can be found here: https://pastebin.com/pGNp3hHY
- Just run this on your linux box passing the token value (expected to be
- hexadecimal), and it will calculate and print the corresponding code.
- Then you can log in to the secrebox server, pass the token from that
- session to the key generator, and enter the correct code. You will get the
- flag printed on screen.
- But hey. Couldn't we just extract the flag from the binary? No, because it's
- not included. So how is the flag retrieved? What happens when you enter the
- correct code, actually?
- Let's jump back to the main function. If verfiy_code returns TRUE, the jump
- after it will not be taken. The code following has a reference to a string
- "CORRECT" and also a "secret.txt", followed by a call to another sub function:
- .text:0804967B call verify_code
- .text:08049680 cmp eax, 0
- .text:08049683 jz loc_80497FC
- .text:08049689 mov eax, 200h
- .text:0804968E xor ecx, ecx
- .text:08049690 mov edx, ds:stdscr
- .text:08049696 mov [esp], edx ; WINDOW *
- .text:08049699 mov dword ptr [esp+4], 200h ; attr_t
- .text:080496A1 mov dword ptr [esp+8], 0 ; void *
- .text:080496A9 mov [ebp+var_11C], eax
- .text:080496AF mov [ebp+var_120], ecx
- .text:080496B5 call _wattr_on
- .text:080496BA mov ecx, 3
- .text:080496BF mov edx, 28h ; '('
- .text:080496C4 lea esi, aCorrect ; "CORRECT"
- .text:080496CA mov dword ptr [esp], 3 ; int
- .text:080496D1 mov dword ptr [esp+4], 28h ; '(' ; int
- .text:080496D9 mov [esp+8], esi ; char *
- .text:080496DD mov [ebp+var_124], eax
- .text:080496E3 mov [ebp+var_128], ecx
- .text:080496E9 mov [ebp+var_12C], edx
- .text:080496EF call _mvprintw
- .text:080496F4 lea ecx, aSecretTxt ; "secret.txt"
- .text:080496FA mov [esp], ecx ; filename
- .text:080496FD mov [ebp+var_130], eax
- .text:08049703 call sub_8048DA0
- The purpose of that function is easily identified. It opens a file, gets its
- size, calls 'malloc' with that size, and reads the contents of that file into
- that buffer. That's it. After returning from that function, you can see it
- will do some ncurses printing that will eventually print whatever is in that
- secret.txt file:
- .text:08049708 mov ecx, 5
- .text:0804970D mov edx, 1Eh
- .text:08049712 lea esi, asc_8049F7C ; "+---------------------------------+"
- .text:08049718 mov [ebp+var_28], eax
- .text:0804971B mov dword ptr [esp], 5 ; int
- .text:08049722 mov dword ptr [esp+4], 1Eh ; int
- .text:0804972A mov [esp+8], esi ; char *
- .text:0804972E mov [ebp+var_134], edx
- .text:08049734 mov [ebp+var_138], ecx
- .text:0804973A call _mvprintw
- .text:0804973F mov ecx, 6
- .text:08049744 mov edx, 1Eh
- .text:08049749 lea esi, a30s ; "|%30s |"
- .text:0804974F mov edi, [ebp+var_28]
- .text:08049752 mov dword ptr [esp], 6 ; int
- .text:08049759 mov dword ptr [esp+4], 1Eh ; int
- .text:08049761 mov [esp+8], esi ; char *
- .text:08049765 mov [esp+0Ch], edi
- .text:08049769 mov [ebp+var_13C], eax
- .text:0804976F mov [ebp+var_140], ecx
- .text:08049775 mov [ebp+var_144], edx
- .text:0804977B call _mvprintw
- .text:08049780 mov ecx, 7
- .text:08049785 mov edx, 1Eh
- .text:0804978A lea esi, asc_8049F7C ; "+---------------------------------+"
- .text:08049790 mov dword ptr [esp], 7 ; int
- .text:08049797 mov dword ptr [esp+4], 1Eh ; int
- .text:0804979F mov [esp+8], esi ; char *
- .text:080497A3 mov [ebp+var_148], eax
- .text:080497A9 mov [ebp+var_14C], ecx
- .text:080497AF mov [ebp+var_150], edx
- .text:080497B5 call _mvprintw
- So if you enter the correct code, the output will look like this:
- SAFEBOX KEYPAD
- Token: A1F0
- ┌───────────────────┐
- │ 002530 │ CORRECT
- │ │
- │ [0] [1] [2] [3] │ +---------------------------------+
- │ [4] [5] [6] [7] │ | flag{welc0me_to_H1T13}
- │ [8] [9] [A] [B] │ +---------------------------------+
- │ [C] [D] [E] [F] │
- │ │
- │ │ 1 1 1 1 1 1
- └───────────────────┘
- You've pressed [0]
- That's it! Now you know the flag :)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement