Sheb-Teth (LayerOne 2017 CTF)
We get a binary! I don't remember any more hints to it than just that. There was flavor text, but I've lost it.
If we strings Sheb-Teth.bin
we'll see some helpfully embedded strings to guide us in the right direction:
[snip] ... [^_] [^_] #.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#. . @emptymonkey https://github.com/emptymonkey 2017-03-10 # # . . Welcome to the LayerOne 2017 CTF "Sheb-Teth" RE challenge. # # There are six flags. flag_0, flag_1, ..., flag_5. . . Each flag is in a uuid format. e.g. c8da2132-382e-11e7-9862-507b9d8156b4 # # When prompted, enter the flag you would like to test. . . This program will tell you if the flag you entered is a valid flag. # # (You know how to speak R'lyehian, right?!) . . Enjoy! # .#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#.# uln sheb teth n'ghaoth sll'ha throd mglw'nafh fhthagn-ngah cf'ayak 'vulgtmm vugtlag'n mnahn' r'luh ... [snip]
Bold added by me, for emphasis. It probably would have been a good idea to check out that github link, but I totally tunnel vision'd over it at first.
So, we know this binary is what we'll be getting used to for all six flags in Sheb-Teth.
Flag 0
Continuing to look through strings
output we'll find f36c454e-3831-11e7-a8b5-3fb45ccb41a5
, and if we try to run the program with that as input... we'll find it quits with an error message:
Unclean mortal! Do not return until you wield *true* power!!
.. wtf, right?
Opening this up in radare2 and looking at the start of sym.main
: (for those playing along at home, aaa
to look for functions, then pdf @ sym.main
to print the disassembled function at the symbol main)
;-- main: / (fcn) sym.main 654 | sym.main (); | ; var int local_38h @ ebp-0x38 | ; var int local_34h @ ebp-0x34 | ; var int local_30h @ ebp-0x30 | ; var int local_2ch @ ebp-0x2c | ; var int local_28h @ ebp-0x28 | ; var int local_24h @ ebp-0x24 | ; var int local_20h @ ebp-0x20 | ; var int local_1ch @ ebp-0x1c | ; var int local_18h @ ebp-0x18 | ; var int local_14h @ ebp-0x14 | ; var int local_10h @ ebp-0x10 | ; var int local_ch @ ebp-0xc | ; var int local_4h_2 @ ebp-0x4 | ; var int local_4h @ esp+0x4 | ; DATA XREF from 0x08048837 (entry0) | 0x0804891b 8d4c2404 lea ecx, [local_4h] | 0x0804891f 83e4f0 and esp, 0xfffffff0 | 0x08048922 ff71fc push dword [ecx - 4] | 0x08048925 55 push ebp | 0x08048926 89e5 mov ebp, esp | 0x08048928 51 push ecx | 0x08048929 83ec34 sub esp, 0x34 ; '4' | 0x0804892c c745f08ca404. mov dword [local_10h], str.uln_sheb_teth__n_ghaoth__sll_ha_throd | 0x08048933 c745ecb4a404. mov dword [local_14h], str.mglw_nafh_fhthagn_ngah_cf_ayak__vulgtmm_vugtlag_n | 0x0804893a c745e8e6a404. mov dword [local_18h], str.mnahn__r_luh | 0x08048941 c745e4f4a404. mov dword [local_1ch], str.r_luh_chtenff__sll_ha_syha_h__y_hah | 0x08048948 c745e018a504. mov dword [local_20h], str.n_ghft_hlirgh_lloig__shuggoth__zhro | 0x0804894f c745dc3ca504. mov dword [local_24h], str.ch_goka_gotha__uaaah | 0x08048956 c745d8000000. mov dword [local_28h], 0 | 0x0804895d c745f4000000. mov dword [local_ch], 0 | 0x08048964 e847fdffff call sym.imp.getuid ; uid_t getuid(void) | 0x08048969 85c0 test eax, eax | ,=< 0x0804896b 7421 je 0x804898e | | 0x0804896d a1a8b10408 mov eax, dword obj.stderr ; loc.stderr ; [0x804b1a8:4]=0x3a434347 ; "GCC: (Debian 4.9.2-10) 4.9.2" | | 0x08048972 50 push eax | | 0x08048973 6a3d push 0x3d ; '=' ; '=' | | 0x08048975 6a01 push 1 ; "ELF\x01\x01\x01" | | 0x08048977 6854a50408 push str.Unclean_mortal__Do_not_return_until_you_wield__true__power___n ; 0x804a554 ; "Unclean mortal! Do not return until you wield *true* power!!\n" | | 0x0804897c e84ffdffff call sym.imp.fwrite ; size_t fwrite(const void *ptr, size_t size, size_t nitems, FILE *stream) | | 0x08048981 83c410 add esp, 0x10 | | 0x08048984 83ec0c sub esp, 0xc | | 0x08048987 6aff push 0xffffffffffffffff | | 0x08048989 e8a2fdffff call sym.imp.exit ; void exit(int status) | | ; JMP XREF from 0x0804896b (sym.main) | `-> 0x0804898e 83ec08 sub esp, 8 | 0x08048991 6a01 push 1 ; "ELF\x01\x01\x01" | 0x08048993 6a25 push 0x25 ; '%' ; '%' | 0x08048995 e876feffff call sym.imp.calloc ; void *calloc(size_t nmeb, size_t size) | 0x0804899a 83c410 add esp, 0x10 | 0x0804899d 8945d4 mov dword [local_2ch], eax | 0x080489a0 837dd400 cmp dword [local_2ch], 0 | ,=< 0x080489a4 7530 jne 0x80489d6 | | 0x080489a6 e8d5fdffff call sym.imp.__errno_location | | 0x080489ab 8b00 mov eax, dword [eax] | | 0x080489ad c745d0ffffff. mov dword [local_30h], 0xffffffff | | 0x080489b4 8945cc mov dword [local_34h], eax | | 0x080489b7 c745c892a504. mov dword [local_38h], str.calloc__d___d_ | | 0x080489be 83ec0c sub esp, 0xc | | 0x080489c1 6a01 push 1 ; "ELF\x01\x01\x01" | | 0x080489c3 6a25 push 0x25 ; '%' ; '%' | | 0x080489c5 ff75c8 push dword [local_38h] | | 0x080489c8 ff75cc push dword [local_34h] | | 0x080489cb ff75d0 push dword [local_30h] | | 0x080489ce e81dfdffff call sym.imp.error | | 0x080489d3 83c420 add esp, 0x20 | | ; JMP XREF from 0x080489a4 (sym.main) | `-> 0x080489d6 83ec08 sub esp, 8 | 0x080489d9 ff75f0 push dword [local_10h] | 0x080489dc 68a1a50408 push str._n_s_n ; 0x804a5a1 ; "\n%s\n" | 0x080489e1 e88afcffff call sym.imp.printf ; int printf(const char *format) | 0x080489e6 83c410 add esp, 0x10 | 0x080489e9 83ec08 sub esp, 8 | 0x080489ec ff75ec push dword [local_14h] | 0x080489ef 68a6a50408 push str._n_s_n_ ; 0x804a5a6 ; "\n%s\n> " | 0x080489f4 e877fcffff call sym.imp.printf ; int printf(const char *format) | 0x080489f9 83c410 add esp, 0x10 | 0x080489fc a1acb10408 mov eax, dword obj.stdin ; [0x804b1ac:4]=0x65442820 ; " (Debian 4.9.2-10) 4.9.2" | 0x08048a01 83ec04 sub esp, 4 | 0x08048a04 50 push eax | 0x08048a05 6a25 push 0x25 ; '%' ; '%' | 0x08048a07 ff75d4 push dword [local_2ch] | 0x08048a0a e881fcffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream) | 0x08048a0f 83c410 add esp, 0x10 | 0x08048a12 85c0 test eax, eax | ,=< 0x08048a14 750a jne 0x8048a20 | | 0x08048a16 b8ffffffff mov eax, 0xffffffff ; -1 | ,==< 0x08048a1b e981010000 jmp 0x8048ba1 | || ; JMP XREF from 0x08048a14 (sym.main) | |`-> 0x08048a20 83ec0c sub esp, 0xc | | 0x08048a23 6a0a push 0xa | | 0x08048a25 e846fdffff call sym.imp.putchar ; int putchar(int c) | | 0x08048a2a 83c410 add esp, 0x10 | | 0x08048a2d 83ec08 sub esp, 8 | | 0x08048a30 6a0a push 0xa | | 0x08048a32 ff75d4 push dword [local_2ch] | | 0x08048a35 e816fdffff call sym.imp.strchr ; char*strchr(char *s, int c) | | 0x08048a3a 83c410 add esp, 0x10 | | 0x08048a3d 8945d8 mov dword [local_28h], eax | | 0x08048a40 837dd800 cmp dword [local_28h], 0 | |,=< 0x08048a44 7406 je 0x8048a4c | || 0x08048a46 8b45d8 mov eax, dword [local_28h] | || 0x08048a49 c60000 mov byte [eax], 0 | || ; JMP XREF from 0x08048a44 (sym.main) | |`-> 0x08048a4c 83ec0c sub esp, 0xc | | 0x08048a4f ff75d4 push dword [local_2ch] | | 0x08048a52 e852010000 call sym.check_flag_0 | | 0x08048a57 83c410 add esp, 0x10 | | 0x08048a5a 85c0 test eax, eax | |,=< 0x08048a5c 741c je 0x8048a7a | || 0x08048a5e 83ec0c sub esp, 0xc | || 0x08048a61 68ada50408 push str.flag_0:_Confirmed_ ; 0x804a5ad ; "flag 0: Confirmed!" | || 0x08048a66 e895fcffff call sym.imp.puts ; int puts(const char *s) | || 0x08048a6b 83c410 add esp, 0x10 | || 0x08048a6e c745f4010000. mov dword [local_ch], 1 | ,===< 0x08048a75 e9d8000000 jmp 0x8048b52 | ||| ; JMP XREF from 0x08048a5c (sym.main) | ||`-> 0x08048a7a 83ec0c sub esp, 0xc | || 0x08048a7d ff75d4 push dword [local_2ch] | || 0x08048a80 e856010000 call sym.check_flag_1 | || 0x08048a85 83c410 add esp, 0x10 | || 0x08048a88 85c0 test eax, eax | ||,=< 0x08048a8a 741c je 0x8048aa8 | ||| 0x08048a8c 83ec0c sub esp, 0xc | ||| 0x08048a8f 68c0a50408 push str.flag_1:_Confirmed_ ; 0x804a5c0 ; "flag 1: Confirmed!" | ||| 0x08048a94 e867fcffff call sym.imp.puts ; int puts(const char *s) | ||| 0x08048a99 83c410 add esp, 0x10 | ||| 0x08048a9c c745f4010000. mov dword [local_ch], 1 | ,====< 0x08048aa3 e9aa000000 jmp 0x8048b52 | |||| ; JMP XREF from 0x08048a8a (sym.main) | |||`-> 0x08048aa8 83ec0c sub esp, 0xc | ||| 0x08048aab ff75d4 push dword [local_2ch] | ||| 0x08048aae e83a030000 call sym.check_flag_2 | ||| 0x08048ab3 83c410 add esp, 0x10 | ||| 0x08048ab6 85c0 test eax, eax | |||,=< 0x08048ab8 7419 je 0x8048ad3 | |||| 0x08048aba 83ec0c sub esp, 0xc | |||| 0x08048abd 68d3a50408 push str.flag_2:_Confirmed_ ; 0x804a5d3 ; "flag 2: Confirmed!" | |||| 0x08048ac2 e839fcffff call sym.imp.puts ; int puts(const char *s) | |||| 0x08048ac7 83c410 add esp, 0x10 | |||| 0x08048aca c745f4010000. mov dword [local_ch], 1 | ,=====< 0x08048ad1 eb7f jmp 0x8048b52 | ||||| ; JMP XREF from 0x08048ab8 (sym.main) | ||||`-> 0x08048ad3 83ec0c sub esp, 0xc | |||| 0x08048ad6 ff75d4 push dword [local_2ch] | |||| 0x08048ad9 e85d050000 call sym.check_flag_3 | |||| 0x08048ade 83c410 add esp, 0x10 | |||| 0x08048ae1 85c0 test eax, eax | ||||,=< 0x08048ae3 7419 je 0x8048afe | ||||| 0x08048ae5 83ec0c sub esp, 0xc | ||||| 0x08048ae8 68e6a50408 push str.flag_3:_Confirmed_ ; 0x804a5e6 ; "flag 3: Confirmed!" | ||||| 0x08048aed e80efcffff call sym.imp.puts ; int puts(const char *s) | ||||| 0x08048af2 83c410 add esp, 0x10 | ||||| 0x08048af5 c745f4010000. mov dword [local_ch], 1 | ,======< 0x08048afc eb54 jmp 0x8048b52 | |||||| ; JMP XREF from 0x08048ae3 (sym.main) | |||||`-> 0x08048afe 83ec0c sub esp, 0xc | ||||| 0x08048b01 ff75d4 push dword [local_2ch] | ||||| 0x08048b04 e8d5060000 call sym.check_flag_4 | ||||| 0x08048b09 83c410 add esp, 0x10 | ||||| 0x08048b0c 85c0 test eax, eax | |||||,=< 0x08048b0e 7419 je 0x8048b29 | |||||| 0x08048b10 83ec0c sub esp, 0xc | |||||| 0x08048b13 68f9a50408 push str.flag_4:_Confirmed_ ; 0x804a5f9 ; "flag 4: Confirmed!" | |||||| 0x08048b18 e8e3fbffff call sym.imp.puts ; int puts(const char *s) | |||||| 0x08048b1d 83c410 add esp, 0x10 | |||||| 0x08048b20 c745f4010000. mov dword [local_ch], 1 | ,=======< 0x08048b27 eb29 jmp 0x8048b52 | ||||||| ; JMP XREF from 0x08048b0e (sym.main) | ||||||`-> 0x08048b29 83ec0c sub esp, 0xc | |||||| 0x08048b2c ff75d4 push dword [local_2ch] | |||||| 0x08048b2f e8680a0000 call sym.check_flag_5 | |||||| 0x08048b34 83c410 add esp, 0x10 | |||||| 0x08048b37 85c0 test eax, eax | ||||||,=< 0x08048b39 7417 je 0x8048b52 | ||||||| 0x08048b3b 83ec0c sub esp, 0xc | ||||||| 0x08048b3e 680ca60408 push str.flag_5:_Confirmed_ ; 0x804a60c ; "flag 5: Confirmed!" | ||||||| 0x08048b43 e8b8fbffff call sym.imp.puts ; int puts(const char *s) | ||||||| 0x08048b48 83c410 add esp, 0x10 | ||||||| 0x08048b4b c745f4010000. mov dword [local_ch], 1 | ||||||| ; XREFS: JMP 0x08048b27 JMP 0x08048afc JMP 0x08048ad1 | ||||||| ; XREFS: JMP 0x08048aa3 JMP 0x08048a75 JMP 0x08048b39 | `````-`-> 0x08048b52 837df400 cmp dword [local_ch], 0 | |,=< 0x08048b56 7423 je 0x8048b7b | || 0x08048b58 83ec08 sub esp, 8 | || 0x08048b5b ff75e4 push dword [local_1ch] | || 0x08048b5e 68a1a50408 push str._n_s_n ; 0x804a5a1 ; "\n%s\n" | || 0x08048b63 e808fbffff call sym.imp.printf ; int printf(const char *format) | || 0x08048b68 83c410 add esp, 0x10 | || 0x08048b6b 83ec0c sub esp, 0xc | || 0x08048b6e ff75dc push dword [local_24h] | || 0x08048b71 e88afbffff call sym.imp.puts ; int puts(const char *s) | || 0x08048b76 83c410 add esp, 0x10 | ,===< 0x08048b79 eb21 jmp 0x8048b9c | ||| ; JMP XREF from 0x08048b56 (sym.main) | ||`-> 0x08048b7b 83ec08 sub esp, 8 | || 0x08048b7e ff75e8 push dword [local_18h] | || 0x08048b81 68a1a50408 push str._n_s_n ; 0x804a5a1 ; "\n%s\n" | || 0x08048b86 e8e5faffff call sym.imp.printf ; int printf(const char *format) | || 0x08048b8b 83c410 add esp, 0x10 | || 0x08048b8e 83ec0c sub esp, 0xc | || 0x08048b91 ff75e0 push dword [local_20h] | || 0x08048b94 e867fbffff call sym.imp.puts ; int puts(const char *s) | || 0x08048b99 83c410 add esp, 0x10 | || ; JMP XREF from 0x08048b79 (sym.main) | `---> 0x08048b9c b800000000 mov eax, 0 | | ; JMP XREF from 0x08048a1b (sym.main) | `--> 0x08048ba1 8b4dfc mov ecx, dword [local_4h_2] | 0x08048ba4 c9 leave | 0x08048ba5 8d61fc lea esp, [ecx - 4] \ 0x08048ba8 c3 ret
in main it puts some strings in locals, which is fine, whatever... and then calls getuid(), followed by testing if the returned uid is 0 (eg, root). So if this program is not run as root, it'll write one 0x3d
byte string to stderr, the Unclean mortal!
thing we saw before.
I dunno about you, but I don't want to run this as root. I don't know GDB too well, and didn't want to figure out how to script it, so the easier option is to patch1 the binary. So I replaced the 0x74 at file offset 0x96b with 0xeb, which changes je 0x804898e
into jmp 0x804898e
.
With that out of the way, and the patch applied, we can run the program, enter the flag from before, and see it is, in fact, a flag.
+50.
(yes, you can easily see the string in the diassembly too)
.. and remember to actually submit the flag, or someone else on your team might do it first! I say, speaking from personal experience..
Flag 1
No more flags in the strings output, it's about time to start looking a little more in-depth at this thing.
Going back to main
, we can see the gist of this program is:
- getuid() to check for root
- printf() some lovecrafty text
- fgets() to read a string
- print a "\n" after user input
- strchr() the input for '\n' (no idea why!)
if (check_flag_0(input)) { puts("confirmed"); return }
if (check_flag_1(input)) { puts("confirmed"); return }
if (check_flag_2(input)) { puts("confirmed"); return }
if (check_flag_3(input)) { puts("confirmed"); return }
if (check_flag_4(input)) { puts("confirmed"); return }
if (check_flag_5(input)) { puts("confirmed"); return }
which means we can look at each flag check independently, and hopefully none of the check_flag_N
functions interact with each other or some global state. By luck and the benevolence of emptymonkey, they happen to not, and we can in fact look at the flag checking functions independently.
pdf @ sym.check_flag_1
(most of it, anyway):
/ (fcn) sym.check_flag_1 530 | sym.check_flag_1 (int arg_8h, ); | ; var int local_4h @ ebp-0x4 | ; arg int arg_8h @ ebp+0x8 | ; var int local_28h @ ebp-0x28 | ; var int local_24h @ ebp-0x24 | ; var int local_20h @ ebp-0x20 | ; var int local_1ch @ ebp-0x1c | ; var int local_18h @ ebp-0x18 | ; var int local_14h @ ebp-0x14 | ; var int local_10h @ ebp-0x10 | ; var int local_ch @ ebp-0xc | ; var int local_4dh @ ebp-0x4d | ; var int local_4eh @ ebp-0x4e | ; var int local_4fh @ ebp-0x4f | ; var int local_50h @ ebp-0x50 | ; var int local_51h @ ebp-0x51 | ; var int local_52h @ ebp-0x52 | ; var int local_53h @ ebp-0x53 | ; var int local_54h @ ebp-0x54 | ; var int local_55h @ ebp-0x55 | ; var int local_56h @ ebp-0x56 | ; var int local_57h @ ebp-0x57 | ; var int local_58h @ ebp-0x58 | ; var int local_59h @ ebp-0x59 | ; var int local_5ah @ ebp-0x5a | ; var int local_5bh @ ebp-0x5b | ; var int local_5ch @ ebp-0x5c | ; var int local_5dh @ ebp-0x5d | ; var int local_5eh @ ebp-0x5e | ; var int local_5fh @ ebp-0x5f | ; var int local_60h @ ebp-0x60 | ; var int local_61h @ ebp-0x61 | ; var int local_62h @ ebp-0x62 | ; var int local_63h @ ebp-0x63 | ; var int local_64h @ ebp-0x64 | ; var int local_65h @ ebp-0x65 | ; var int local_66h @ ebp-0x66 | ; var int local_67h @ ebp-0x67 | ; var int local_68h @ ebp-0x68 | ; var int local_69h @ ebp-0x69 | ; var int local_6ah @ ebp-0x6a | ; var int local_6bh @ ebp-0x6b | ; var int local_6ch @ ebp-0x6c | ; var int local_6dh @ ebp-0x6d | ; var int local_6eh @ ebp-0x6e | ; var int local_6fh @ ebp-0x6f | ; var int local_70h @ ebp-0x70 | ; var int local_29h @ ebp-0x29 | ; var int local_2ah @ ebp-0x2a | ; var int local_2bh @ ebp-0x2b | ; var int local_2ch @ ebp-0x2c | ; var int local_2dh @ ebp-0x2d | ; var int local_2eh @ ebp-0x2e | ; var int local_2fh @ ebp-0x2f | ; var int local_30h @ ebp-0x30 | ; var int local_31h @ ebp-0x31 | ; var int local_32h @ ebp-0x32 | ; var int local_33h @ ebp-0x33 | ; var int local_34h @ ebp-0x34 | ; var int local_35h @ ebp-0x35 | ; var int local_36h @ ebp-0x36 | ; var int local_37h @ ebp-0x37 | ; var int local_38h @ ebp-0x38 | ; var int local_39h @ ebp-0x39 | ; var int local_3ah @ ebp-0x3a | ; var int local_3bh @ ebp-0x3b | ; var int local_3ch @ ebp-0x3c | ; var int local_3dh @ ebp-0x3d | ; var int local_3eh @ ebp-0x3e | ; var int local_3fh @ ebp-0x3f | ; var int local_40h @ ebp-0x40 | ; var int local_41h @ ebp-0x41 | ; var int local_42h @ ebp-0x42 | ; var int local_43h @ ebp-0x43 | ; var int local_44h @ ebp-0x44 | ; var int local_45h @ ebp-0x45 | ; var int local_46h @ ebp-0x46 | ; var int local_47h @ ebp-0x47 | ; var int local_48h @ ebp-0x48 | ; var int local_49h @ ebp-0x49 | ; var int local_4ah @ ebp-0x4a | ; var int local_4bh @ ebp-0x4b | ; var int local_4ch @ ebp-0x4c | ; CALL XREF from 0x08048a80 (sym.main) | 0x08048bdb 55 push ebp ; .//flag_0.c:14 | 0x08048bdc 89e5 mov ebp, esp | 0x08048bde 53 push ebx | 0x08048bdf 83ec74 sub esp, 0x74 ; 't' | 0x08048be2 c645b4e4 mov byte [local_4ch], 0xe4 ; .//flag_1.c:9 | 0x08048be6 c645b543 mov byte [local_4bh], 0x43 ; 'C' | 0x08048bea c645b651 mov byte [local_4ah], 0x51 ; 'Q' | 0x08048bee c645b750 mov byte [local_49h], 0x50 ; 'P' | 0x08048bf2 c645b8c2 mov byte [local_48h], 0xc2 | 0x08048bf6 c645b918 mov byte [local_47h], 0x18 | 0x08048bfa c645bae4 mov byte [local_46h], 0xe4 | 0x08048bfe c645bb25 mov byte [local_45h], 0x25 ; '%' | 0x08048c02 c645bc83 mov byte [local_44h], 0x83 | 0x08048c06 c645bdc7 mov byte [local_43h], 0xc7 | 0x08048c0a c645bee2 mov byte [local_42h], 0xe2 | 0x08048c0e c645bf3c mov byte [local_41h], 0x3c ; '<' | 0x08048c12 c645c0fd mov byte [local_40h], 0xfd | 0x08048c16 c645c1db mov byte [local_3fh], 0xdb | 0x08048c1a c645c21b mov byte [local_3eh], 0x1b | 0x08048c1e c645c323 mov byte [local_3dh], 0x23 ; '#' | 0x08048c22 c645c490 mov byte [local_3ch], 0x90 | 0x08048c26 c645c518 mov byte [local_3bh], 0x18 | 0x08048c2a c645c6ce mov byte [local_3ah], 0xce | 0x08048c2e c645c71c mov byte [local_39h], 0x1c | 0x08048c32 c645c81f mov byte [local_38h], 0x1f | 0x08048c36 c645c9de mov byte [local_37h], 0xde | 0x08048c3a c645caf2 mov byte [local_36h], 0xf2 | 0x08048c3e c645cb20 mov byte [local_35h], 0x20 | 0x08048c42 c645cc36 mov byte [local_34h], 0x36 ; '6' | 0x08048c46 c645cd5e mov byte [local_33h], 0x5e ; '^' | 0x08048c4a c645ce1d mov byte [local_32h], 0x1d | 0x08048c4e c645cf63 mov byte [local_31h], 0x63 ; 'c' | 0x08048c52 c645d095 mov byte [local_30h], 0x95 | 0x08048c56 c645d127 mov byte [local_2fh], 0x27 ; ''' | 0x08048c5a c645d216 mov byte [local_2eh], 0x16 | 0x08048c5e c645d310 mov byte [local_2dh], 0x10 | 0x08048c62 c645d4d8 mov byte [local_2ch], 0xd8 | 0x08048c66 c645d5b5 mov byte [local_2bh], 0xb5 | 0x08048c6a c645d6de mov byte [local_2ah], 0xde | 0x08048c6e c645d713 mov byte [local_29h], 0x13 | 0x08048c72 c6459081 mov byte [local_70h], 0x81 ; .//flag_1.c:10 | 0x08048c76 c6459173 mov byte [local_6fh], 0x73 ; 's' | 0x08048c7a c6459230 mov byte [local_6eh], 0x30 ; '0' | 0x08048c7e c6459367 mov byte [local_6dh], 0x67 ; 'g' | 0x08048c82 c64594f5 mov byte [local_6ch], 0xf5 | 0x08048c86 c645957c mov byte [local_6bh], 0x7c ; '|' | 0x08048c8a c6459685 mov byte [local_6ah], 0x85 | 0x08048c8e c6459746 mov byte [local_69h], 0x46 ; 'F' | 0x08048c92 c64598ae mov byte [local_68h], 0xae | 0x08048c96 c64599f4 mov byte [local_67h], 0xf4 | 0x08048c9a c6459ada mov byte [local_66h], 0xda | 0x08048c9e c6459b0f mov byte [local_65h], 0xf | 0x08048ca2 c6459ccc mov byte [local_64h], 0xcc | 0x08048ca6 c6459df6 mov byte [local_63h], 0xf6 | 0x08048caa c6459e2a mov byte [local_62h], 0x2a ; '*' | 0x08048cae c6459f12 mov byte [local_61h], 0x12 | 0x08048cb2 c645a0f5 mov byte [local_60h], 0xf5 | 0x08048cb6 c645a12f mov byte [local_5fh], 0x2f ; '/' | 0x08048cba c645a2e3 mov byte [local_5eh], 0xe3 | 0x08048cbe c645a37d mov byte [local_5dh], 0x7d ; '}' | 0x08048cc2 c645a47d mov byte [local_5ch], 0x7d ; '}' | 0x08048cc6 c645a5ec mov byte [local_5bh], 0xec | 0x08048cca c645a6ca mov byte [local_5ah], 0xca | 0x08048cce c645a70d mov byte [local_59h], 0xd | 0x08048cd2 c645a855 mov byte [local_58h], 0x55 ; 'U' | 0x08048cd6 c645a938 mov byte [local_57h], 0x38 ; '8' | 0x08048cda c645aa28 mov byte [local_56h], 0x28 ; '(' | 0x08048cde c645ab50 mov byte [local_55h], 0x50 ; 'P' | 0x08048ce2 c645aca6 mov byte [local_54h], 0xa6 | 0x08048ce6 c645ad15 mov byte [local_53h], 0x15 | 0x08048cea c645ae70 mov byte [local_52h], 0x70 ; 'p' | 0x08048cee c645af76 mov byte [local_51h], 0x76 ; 'v' | 0x08048cf2 c645b0bd mov byte [local_50h], 0xbd | 0x08048cf6 c645b1d1 mov byte [local_4fh], 0xd1 | 0x08048cfa c645b2eb mov byte [local_4eh], 0xeb | 0x08048cfe c645b371 mov byte [local_4dh], 0x71 ; 'q' | 0x08048d02 c745f4000000. mov dword [local_ch], 0 ; .//flag_1.c:12 | 0x08048d09 83ec08 sub esp, 8 ; .//flag_1.c:16 | 0x08048d0c 6a18 push 0x18 ; " \x88\x04\b4" | 0x08048d0e 6a01 push 1 | 0x08048d10 e8fbfaffff call sym.imp.calloc ; void *calloc(size_t nmeb, size_t size) | 0x08048d15 83c410 add esp, 0x10 | 0x08048d18 8945f0 mov dword [local_10h], eax | 0x08048d1b 837df000 cmp dword [local_10h], 0 | ,=< 0x08048d1f 752b jne 0x8048d4c | | 0x08048d21 e85afaffff call sym.imp.__errno_location ; .//flag_1.c:17 | | 0x08048d26 8b00 mov eax, dword [eax] | | 0x08048d28 c745ecffffff. mov dword [local_14h], 0xffffffff | | 0x08048d2f 8945e8 mov dword [local_18h], eax | | 0x08048d32 c745e445a604. mov dword [local_1ch], 0x804a645 | | 0x08048d39 6a18 push 0x18 ; /usr/include/i386-linux-gnu/bits/error.h:42 ; " \x88\x04\b4" | | 0x08048d3b ff75e4 push dword [local_1ch] | | 0x08048d3e ff75e8 push dword [local_18h] | | 0x08048d41 ff75ec push dword [local_14h] | | 0x08048d44 e8a7f9ffff call sym.imp.error | | 0x08048d49 83c410 add esp, 0x10 | | ; JMP XREF from 0x08048d1f (sym.check_flag_1) | `-> 0x08048d4c 8b45f0 mov eax, dword [local_10h] ; .//flag_1.c:20 | 0x08048d4f c70024000000 mov dword [eax], 0x24 ; '$' ; [0x24:4]=0 ; '$' | 0x08048d55 8b45f0 mov eax, dword [local_10h] ; .//flag_1.c:21 | 0x08048d58 8d55b4 lea edx, [local_4ch] | 0x08048d5b 895008 mov dword [eax + 8], edx | 0x08048d5e 8b45f0 mov eax, dword [local_10h] ; .//flag_1.c:22 | 0x08048d61 8d5590 lea edx, [local_70h] | 0x08048d64 89500c mov dword [eax + 0xc], edx | 0x08048d67 83ec0c sub esp, 0xc ; .//flag_1.c:24 | 0x08048d6a ff75f0 push dword [local_10h] | 0x08048d6d e86e0e0000 call sym.xorscura_decrypt | 0x08048d72 83c410 add esp, 0x10 | 0x08048d75 83f8ff cmp eax, 0xffffffffffffffff | ,=< 0x08048d78 752d jne 0x8048da7 | | 0x08048d7a 8b5df0 mov ebx, dword [local_10h] ; .//flag_1.c:25 | | 0x08048d7d e8fef9ffff call sym.imp.__errno_location | | 0x08048d82 8b00 mov eax, dword [eax] | | 0x08048d84 c745e0ffffff. mov dword [local_20h], 0xffffffff | | 0x08048d8b 8945dc mov dword [local_24h], eax | | 0x08048d8e c745d853a604. mov dword [local_28h], 0x804a653 | | 0x08048d95 53 push ebx ; /usr/include/i386-linux-gnu/bits/error.h:42 | | 0x08048d96 ff75d8 push dword [local_28h] | | 0x08048d99 ff75dc push dword [local_24h] | | 0x08048d9c ff75e0 push dword [local_20h] | | 0x08048d9f e84cf9ffff call sym.imp.error | | 0x08048da4 83c410 add esp, 0x10 | | ; JMP XREF from 0x08048d78 (sym.check_flag_1) | `-> 0x08048da7 8b45f0 mov eax, dword [local_10h] ; .//flag_1.c:28 | 0x08048daa 8b4004 mov eax, dword [eax + 4] ; [0x4:4]=0x10101 | 0x08048dad 83ec04 sub esp, 4 | 0x08048db0 6a24 push 0x24 ; '$' ; '$' | 0x08048db2 ff7508 push dword [arg_8h] | 0x08048db5 50 push eax | 0x08048db6 e805faffff call sym.imp.strncmp ; int strncmp(const char *s1, const char *s2, size_t n) | 0x08048dbb 83c410 add esp, 0x10 | 0x08048dbe 85c0 test eax, eax | ,=< 0x08048dc0 7507 jne 0x8048dc9 | | 0x08048dc2 c745f4010000. mov dword [local_ch], 1 ; .//flag_1.c:29 | | ; JMP XREF from 0x08048dc0 (sym.check_flag_1) | `-> 0x08048dc9 83ec0c sub esp, 0xc ; .//flag_1.c:32 | 0x08048dcc ff75f0 push dword [local_10h] | 0x08048dcf e87c100000 call sym.xorscura_free_xod ; void free(void *ptr) | 0x08048dd4 83c410 add esp, 0x10 | 0x08048dd7 83ec0c sub esp, 0xc ; .//flag_1.c:33 | 0x08048dda ff75f0 push dword [local_10h] | 0x08048ddd e89ef8ffff call sym.imp.free ; void free(void *ptr) | 0x08048de2 83c410 add esp, 0x10 | 0x08048de5 8b45f4 mov eax, dword [local_ch] ; .//flag_1.c:35 | 0x08048de8 8b5dfc mov ebx, dword [local_4h] ; .//flag_1.c:36 | 0x08048deb c9 leave \ 0x08048dec c3 ret
Immediately we can see that a bunch of data is being mov
'd onto the stack, followed by a calloc()
, a call to some xorscura_decrypt()
function, and a strncmp()
. Interestingly, the stack data is written one byte at a time, suggesting the compiler didn't want to optimize too hard - going four bytes at a time would save ~81 bytes.
In the interest of lazy, it's probably safe to assume that the decoded flag is being strncmp()
'd with the user input, so it's time to learn how to use GDB a little!
gdb Sheb-Teth.bin
is good to get started, then set a breakpoint at the strncmp()
call of interest with b *0x8048db6
. The *
notation is GDB for specifying an address (rather than a symbol).
Now, r
to run the program, enter some string as input, c
to continue on and hope all goes as planned with us eventually hitting that breakpoint.
We do hit it, and at this point the program is paused immediately before calling strncmp()
, with the stack set up just as it needs to be. x/3x $esp
shows the three 32-bit integers from where esp
points, going upwards with each successive one, which corresponds to strncmp()
arguments in left to right order
0x0804c868
should be one string (which turns out to be the flag)0x0804c008
should be another (which turns out to be ours)0x00000024
is thesize_t n
parameter, the length to compare up to.
Flags are 36-character UUIDs, so 0x24
makes sense. x/s 0x804c008
shows our initial input, so x/s 0x804c868
should show us a flag - which it pleasantly does.
+100.
Flag 2
pdf @ sym.check_flag_2
/ (fcn) sym.check_flag_2 570 | sym.check_flag_2 (int arg_8h, ); | ; var int local_4h @ ebp-0x4 | ; arg int arg_8h @ ebp+0x8 | ; var int local_34h @ ebp-0x34 | ; var int local_30h @ ebp-0x30 | ; var int local_2ch @ ebp-0x2c | ; var int local_28h @ ebp-0x28 | ; var int local_24h @ ebp-0x24 | ; var int local_20h @ ebp-0x20 | ; var int local_1ch @ ebp-0x1c | ; var int local_59h @ ebp-0x59 | ; var int local_10h @ ebp-0x10 | ; var int local_18h @ ebp-0x18 | ; var int local_ch @ ebp-0xc | ; var int local_35h @ ebp-0x35 | ; var int local_36h @ ebp-0x36 | ; var int local_37h @ ebp-0x37 | ; var int local_38h @ ebp-0x38 | ; var int local_39h @ ebp-0x39 | ; var int local_3ah @ ebp-0x3a | ; var int local_3bh @ ebp-0x3b | ; var int local_3ch @ ebp-0x3c | ; var int local_3dh @ ebp-0x3d | ; var int local_3eh @ ebp-0x3e | ; var int local_3fh @ ebp-0x3f | ; var int local_40h @ ebp-0x40 | ; var int local_41h @ ebp-0x41 | ; var int local_42h @ ebp-0x42 | ; var int local_43h @ ebp-0x43 | ; var int local_44h @ ebp-0x44 | ; var int local_45h @ ebp-0x45 | ; var int local_46h @ ebp-0x46 | ; var int local_47h @ ebp-0x47 | ; var int local_48h @ ebp-0x48 | ; var int local_49h @ ebp-0x49 | ; var int local_4ah @ ebp-0x4a | ; var int local_4bh @ ebp-0x4b | ; var int local_4ch @ ebp-0x4c | ; var int local_4dh @ ebp-0x4d | ; var int local_4eh @ ebp-0x4e | ; var int local_4fh @ ebp-0x4f | ; var int local_50h @ ebp-0x50 | ; var int local_51h @ ebp-0x51 | ; var int local_52h @ ebp-0x52 | ; var int local_53h @ ebp-0x53 | ; var int local_54h @ ebp-0x54 | ; var int local_55h @ ebp-0x55 | ; var int local_56h @ ebp-0x56 | ; var int local_57h @ ebp-0x57 | ; var int local_58h @ ebp-0x58 | ; var int local_14h @ ebp-0x14 | ; CALL XREF from 0x08048aae (sym.main) | 0x08048ded 55 push ebp ; .//flag_1.c:36 | 0x08048dee 89e5 mov ebp, esp | 0x08048df0 53 push ebx | 0x08048df1 83ec64 sub esp, 0x64 ; 'd' | 0x08048df4 c745ecea2a38. mov dword [local_14h], 0xcc382aea ; .//flag_2.c:11 | 0x08048dfb c645a895 mov byte [local_58h], 0x95 ; .//flag_2.c:12 | 0x08048dff c645a9c5 mov byte [local_57h], 0xc5 | 0x08048e03 c645aab5 mov byte [local_56h], 0xb5 | 0x08048e07 c645ab15 mov byte [local_55h], 0x15 | 0x08048e0b c645acbb mov byte [local_54h], 0xbb | 0x08048e0f c645add8 mov byte [local_53h], 0xd8 | 0x08048e13 c645ae4b mov byte [local_52h], 0x4b ; 'K' | 0x08048e17 c645af55 mov byte [local_51h], 0x55 ; 'U' | 0x08048e1b c645b035 mov byte [local_50h], 0x35 ; '5' | 0x08048e1f c645b103 mov byte [local_4fh], 3 | 0x08048e23 c645b22b mov byte [local_4eh], 0x2b ; '+' | 0x08048e27 c645b340 mov byte [local_4dh], 0x40 ; '@' | 0x08048e2b c645b429 mov byte [local_4ch], 0x29 ; ')' | 0x08048e2f c645b5c3 mov byte [local_4bh], 0xc3 | 0x08048e33 c645b6de mov byte [local_4ah], 0xde | 0x08048e37 c645b711 mov byte [local_49h], 0x11 | 0x08048e3b c645b891 mov byte [local_48h], 0x91 | 0x08048e3f c645b978 mov byte [local_47h], 0x78 ; 'x' | 0x08048e43 c645bab0 mov byte [local_46h], 0xb0 | 0x08048e47 c645bb5c mov byte [local_45h], 0x5c ; '\' | 0x08048e4b c645bce2 mov byte [local_44h], 0xe2 | 0x08048e4f c645bdee mov byte [local_43h], 0xee | 0x08048e53 c645beb8 mov byte [local_42h], 0xb8 | 0x08048e57 c645bf7f mov byte [local_41h], 0x7f | 0x08048e5b c645c0cd mov byte [local_40h], 0xcd | 0x08048e5f c645c1a5 mov byte [local_3fh], 0xa5 | 0x08048e63 c645c20b mov byte [local_3eh], 0xb | 0x08048e67 c645c366 mov byte [local_3dh], 0x66 ; 'f' | 0x08048e6b c645c4d7 mov byte [local_3ch], 0xd7 | 0x08048e6f c645c5a3 mov byte [local_3bh], 0xa3 | 0x08048e73 c645c673 mov byte [local_3ah], 0x73 ; 's' | 0x08048e77 c645c748 mov byte [local_39h], 0x48 ; 'H' | 0x08048e7b c645c8d7 mov byte [local_38h], 0xd7 | 0x08048e7f c645c9d7 mov byte [local_37h], 0xd7 | 0x08048e83 c645ca74 mov byte [local_36h], 0x74 ; 't' | 0x08048e87 c645cb66 mov byte [local_35h], 0x66 ; 'f' | 0x08048e8b c745f4000000. mov dword [local_ch], 0 ; .//flag_2.c:14 | 0x08048e92 83ec08 sub esp, 8 ; .//flag_2.c:29 | 0x08048e95 6a00 push 0 | 0x08048e97 6869a60408 push str._proc_self_status ; 0x804a669 ; "/proc/self/status" | 0x08048e9c e89ff8ffff call sym.imp.open ; int open(const char *path, int oflag) | 0x08048ea1 83c410 add esp, 0x10 | 0x08048ea4 8945e8 mov dword [local_18h], eax | 0x08048ea7 c745f0070000. mov dword [local_10h], 7 ; .//flag_2.c:32 | ,=< 0x08048eae eb20 jmp 0x8048ed0 ; .//flag_2.c:33 | | ; JMP XREF from 0x08048ed4 (sym.check_flag_2) | .--> 0x08048eb0 83ec04 sub esp, 4 ; .//flag_2.c:34 | || 0x08048eb3 6a01 push 1 | || 0x08048eb5 8d45a7 lea eax, [local_59h] | || 0x08048eb8 50 push eax | || 0x08048eb9 ff75e8 push dword [local_18h] | || 0x08048ebc e89ff7ffff call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | || 0x08048ec1 83c410 add esp, 0x10 | || 0x08048ec4 0fb645a7 movzx eax, byte [local_59h] ; .//flag_2.c:35 | || 0x08048ec8 3c0a cmp al, 0xa | ,===< 0x08048eca 7504 jne 0x8048ed0 | ||| 0x08048ecc 836df001 sub dword [local_10h], 1 ; .//flag_2.c:36 | |!| ; JMP XREF from 0x08048eae (sym.check_flag_2) | |!| ; JMP XREF from 0x08048eca (sym.check_flag_2) | `-`-> 0x08048ed0 837df000 cmp dword [local_10h], 0 ; .//flag_2.c:33 | `==< 0x08048ed4 75da jne 0x8048eb0 | 0x08048ed6 c745f0010000. mov dword [local_10h], 1 ; .//flag_2.c:41 | ,=< 0x08048edd eb20 jmp 0x8048eff ; .//flag_2.c:42 | | ; JMP XREF from 0x08048f03 (sym.check_flag_2) | .--> 0x08048edf 83ec04 sub esp, 4 ; .//flag_2.c:43 | || 0x08048ee2 6a01 push 1 | || 0x08048ee4 8d45a7 lea eax, [local_59h] | || 0x08048ee7 50 push eax | || 0x08048ee8 ff75e8 push dword [local_18h] | || 0x08048eeb e870f7ffff call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | || 0x08048ef0 83c410 add esp, 0x10 | || 0x08048ef3 0fb645a7 movzx eax, byte [local_59h] ; .//flag_2.c:44 | || 0x08048ef7 3c09 cmp al, 9 | ,===< 0x08048ef9 7504 jne 0x8048eff | ||| 0x08048efb 836df001 sub dword [local_10h], 1 ; .//flag_2.c:45 | |!| ; JMP XREF from 0x08048edd (sym.check_flag_2) | |!| ; JMP XREF from 0x08048ef9 (sym.check_flag_2) | `-`-> 0x08048eff 837df000 cmp dword [local_10h], 0 ; .//flag_2.c:42 | `==< 0x08048f03 75da jne 0x8048edf | 0x08048f05 83ec04 sub esp, 4 ; .//flag_2.c:52 | 0x08048f08 6a01 push 1 | 0x08048f0a 8d45a7 lea eax, [local_59h] | 0x08048f0d 50 push eax | 0x08048f0e ff75e8 push dword [local_18h] | 0x08048f11 e84af7ffff call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | 0x08048f16 83c410 add esp, 0x10 | 0x08048f19 83ec0c sub esp, 0xc ; .//flag_2.c:53 | 0x08048f1c ff75e8 push dword [local_18h] | 0x08048f1f e8ccf8ffff call sym.imp.close ; int close(int fildes) | 0x08048f24 83c410 add esp, 0x10 | 0x08048f27 83ec0c sub esp, 0xc ; .//flag_2.c:56 | 0x08048f2a 8d45a7 lea eax, [local_59h] | 0x08048f2d 50 push eax | 0x08048f2e e86df8ffff call sym.imp.atoi ; int atoi(const char *str) | 0x08048f33 83c410 add esp, 0x10 | 0x08048f36 85c0 test eax, eax | ,=< 0x08048f38 7405 je 0x8048f3f | | 0x08048f3a e823090000 call sym.eldritch_function ; .//flag_2.c:57 | | ; JMP XREF from 0x08048f38 (sym.check_flag_2) | `-> 0x08048f3f 83ec08 sub esp, 8 ; .//flag_2.c:61 | 0x08048f42 6a18 push 0x18 ; " \x88\x04\b4" | 0x08048f44 6a01 push 1 | 0x08048f46 e8c5f8ffff call sym.imp.calloc ; void *calloc(size_t nmeb, size_t size) | 0x08048f4b 83c410 add esp, 0x10 | 0x08048f4e 8945e4 mov dword [local_1ch], eax | 0x08048f51 837de400 cmp dword [local_1ch], 0 | ,=< 0x08048f55 752b jne 0x8048f82 | | 0x08048f57 e824f8ffff call sym.imp.__errno_location ; .//flag_2.c:62 | | 0x08048f5c 8b00 mov eax, dword [eax] | | 0x08048f5e c745e0ffffff. mov dword [local_20h], 0xffffffff | | 0x08048f65 8945dc mov dword [local_24h], eax | | 0x08048f68 c745d87ba604. mov dword [local_28h], 0x804a67b | | 0x08048f6f 6a18 push 0x18 ; /usr/include/i386-linux-gnu/bits/error.h:42 ; " \x88\x04\b4" | | 0x08048f71 ff75d8 push dword [local_28h] | | 0x08048f74 ff75dc push dword [local_24h] | | 0x08048f77 ff75e0 push dword [local_20h] | | 0x08048f7a e871f7ffff call sym.imp.error | | 0x08048f7f 83c410 add esp, 0x10 | | ; JMP XREF from 0x08048f55 (sym.check_flag_2) | `-> 0x08048f82 8b45e4 mov eax, dword [local_1ch] ; .//flag_2.c:65 | 0x08048f85 c70024000000 mov dword [eax], 0x24 ; '$' ; [0x24:4]=0 ; '$' | 0x08048f8b 8b45e4 mov eax, dword [local_1ch] ; .//flag_2.c:66 | 0x08048f8e 8b55ec mov edx, dword [local_14h] | 0x08048f91 895010 mov dword [eax + 0x10], edx | 0x08048f94 8b45e4 mov eax, dword [local_1ch] ; .//flag_2.c:67 | 0x08048f97 8d55a8 lea edx, [local_58h] | 0x08048f9a 89500c mov dword [eax + 0xc], edx | 0x08048f9d 83ec0c sub esp, 0xc ; .//flag_2.c:71 | 0x08048fa0 ff75e4 push dword [local_1ch] | 0x08048fa3 e8380c0000 call sym.xorscura_decrypt | 0x08048fa8 83c410 add esp, 0x10 | 0x08048fab 83f8ff cmp eax, 0xffffffffffffffff | ,=< 0x08048fae 752d jne 0x8048fdd | | 0x08048fb0 8b5de4 mov ebx, dword [local_1ch] ; .//flag_2.c:72 | | 0x08048fb3 e8c8f7ffff call sym.imp.__errno_location | | 0x08048fb8 8b00 mov eax, dword [eax] | | 0x08048fba c745d4ffffff. mov dword [local_2ch], 0xffffffff | | 0x08048fc1 8945d0 mov dword [local_30h], eax | | 0x08048fc4 c745cc89a604. mov dword [local_34h], 0x804a689 | | 0x08048fcb 53 push ebx ; /usr/include/i386-linux-gnu/bits/error.h:42 | | 0x08048fcc ff75cc push dword [local_34h] | | 0x08048fcf ff75d0 push dword [local_30h] | | 0x08048fd2 ff75d4 push dword [local_2ch] | | 0x08048fd5 e816f7ffff call sym.imp.error | | 0x08048fda 83c410 add esp, 0x10 | | ; JMP XREF from 0x08048fae (sym.check_flag_2) | `-> 0x08048fdd 8b45e4 mov eax, dword [local_1ch] ; .//flag_2.c:75 | 0x08048fe0 8b10 mov edx, dword [eax] | 0x08048fe2 8b45e4 mov eax, dword [local_1ch] | 0x08048fe5 8b4004 mov eax, dword [eax + 4] ; [0x4:4]=0x10101 | 0x08048fe8 83ec04 sub esp, 4 | 0x08048feb 52 push edx | 0x08048fec 50 push eax | 0x08048fed ff7508 push dword [arg_8h] | 0x08048ff0 e8cbf7ffff call sym.imp.strncmp ; int strncmp(const char *s1, const char *s2, size_t n) | 0x08048ff5 83c410 add esp, 0x10 | 0x08048ff8 85c0 test eax, eax | ,=< 0x08048ffa 7507 jne 0x8049003 | | 0x08048ffc c745f4010000. mov dword [local_ch], 1 ; .//flag_2.c:76 | | ; JMP XREF from 0x08048ffa (sym.check_flag_2) | `-> 0x08049003 83ec0c sub esp, 0xc ; .//flag_2.c:79 | 0x08049006 ff75e4 push dword [local_1ch] | 0x08049009 e8420e0000 call sym.xorscura_free_xod ; void free(void *ptr) | 0x0804900e 83c410 add esp, 0x10 | 0x08049011 83ec0c sub esp, 0xc ; .//flag_2.c:80 | 0x08049014 ff75e4 push dword [local_1ch] | 0x08049017 e864f6ffff call sym.imp.free ; void free(void *ptr) | 0x0804901c 83c410 add esp, 0x10 | 0x0804901f 8b45f4 mov eax, dword [local_ch] ; .//flag_2.c:82 | 0x08049022 8b5dfc mov ebx, dword [local_4h] ; .//flag_2.c:83 | 0x08049025 c9 leave \ 0x08049026 c3 ret
This is where things get more fun. The rest of the challenges involve some measure of anti-debugging, and here we can see:
open()
/proc/self/status
- store that file descriptor at
ebp + 0x18
read()
that file descriptor a bitread()
a little moreread()
one last time into the buffer atebp + 0x59h
. Looking a little more closely shows allnbyte
values for the number of bytes to read is 1 here, and before.close()
the file descriptoratoi()
the byte that was read intoebp + 0x59h
- if
atoi()
returned 0, skip overeldritch_function()
, otherwise call it. - same flag-checking mechanics from here as in
check_flag_1
So, clearly, something is interesting about /proc/self/status
. I'm not familiar with it, so it's time to take a look:
Name: vim
State: R (running)
Tgid: 25100
Ngid: 0
Pid: 25100
PPid: 11187
TracerPid: 0
Uid: 1000 1000 1000 1000
Gid: 1000 1000 1000 1000
[snip]
Most of these fields are clear enough, though I'm not sure about Tgid
, Ngid
, PPid
, or TracerPid
. It's probably safe to assume the gid
are related to the group id of the self
process, where PPid
is probably the parent PID, leaving TracerPid
as an unknown. I'm going to guess that if a debugger is attached to the process, TracerPid probably indicates what's tracing the process, so probably the pid of the debugger attached, with 0 for there isn't a debugger attached
.
Going back to the code, the first read()
use is in a loop counting down from 7, comparing each byte read to 0xa
(eg, "\n"
), so it's seeking down to the 8th line. That would be the Uid
line, which threw me initially. This is right after the TracerPid
line which is significantly more interesting here. I'm quite certain this is actually a bug on most systems but happens to work because Uid
is "supposed" to be 0 (run as root). I patched the 7
in mov dword [local_10h], 7
to 6
so it looks in the right place and doesn't segfault when run as non-root outside a debugger.
Then, the second read()
loop goes forward until it reads a 0x9
("\t") (or a null, but it won't). The fields in /proc/self/status
are tab-delimited, so it's scanning forward to the first number.
The third read()
reads the first digit of whatever number we're at, which will be 0 if the number is 0, or any non-0 digit otherwise, beause these values are not 0-padded.
So when the program atoi()
's whatever digit we read, it'll that number as a value in eax - if the intent was to read the number on the TracerPid
line, this would check for a debugger. As things stand, this is a duplicate uid
check for 0.. I think.
One way or the other, this ends up calling eldritch_function
which ends with a fairly nonsensical ret 0xcfd
, almost guaranteeing a segfualt when it returns. Which it, in fact, does.
Again the fan of binary patching, I replaced the 0x74
at 0xf38
in the binary with 0xeb
, changing the conditional jump into a constant jump, bypassing eldritch_function()
no matter what.
Throw a breakpoint on the strncmp
here at 0x8048ff0
, run again, and x/3x $esp
and follow the two pointers there for x/s 0x804c868
, which prints out another flag!
+200
Flag 3
/ (fcn) sym.check_flag_3 415 | sym.check_flag_3 (int arg_8h, ); | ; var int local_4h @ ebp-0x4 | ; var int local_30h @ ebp-0x30 | ; var int local_2ch @ ebp-0x2c | ; var int local_28h @ ebp-0x28 | ; var int local_18h @ ebp-0x18 | ; arg int arg_8h @ ebp+0x8 | ; var int local_24h @ ebp-0x24 | ; var int local_20h @ ebp-0x20 | ; var int local_1ch @ ebp-0x1c | ; var int local_14h @ ebp-0x14 | ; var int local_ch @ ebp-0xc | ; var int local_10h @ ebp-0x10 | ; var int local_31h @ ebp-0x31 | ; var int local_32h @ ebp-0x32 | ; var int local_33h @ ebp-0x33 | ; var int local_34h @ ebp-0x34 | ; var int local_35h @ ebp-0x35 | ; var int local_36h @ ebp-0x36 | ; var int local_37h @ ebp-0x37 | ; var int local_38h @ ebp-0x38 | ; var int local_39h @ ebp-0x39 | ; var int local_3ah @ ebp-0x3a | ; var int local_3bh @ ebp-0x3b | ; var int local_3ch @ ebp-0x3c | ; var int local_3dh @ ebp-0x3d | ; var int local_3eh @ ebp-0x3e | ; var int local_3fh @ ebp-0x3f | ; var int local_40h @ ebp-0x40 | ; var int local_41h @ ebp-0x41 | ; var int local_42h @ ebp-0x42 | ; var int local_43h @ ebp-0x43 | ; var int local_44h @ ebp-0x44 | ; var int local_45h @ ebp-0x45 | ; var int local_46h @ ebp-0x46 | ; var int local_47h @ ebp-0x47 | ; var int local_48h @ ebp-0x48 | ; var int local_49h @ ebp-0x49 | ; var int local_4ah @ ebp-0x4a | ; var int local_4bh @ ebp-0x4b | ; var int local_4ch @ ebp-0x4c | ; var int local_4dh @ ebp-0x4d | ; var int local_4eh @ ebp-0x4e | ; var int local_4fh @ ebp-0x4f | ; var int local_50h @ ebp-0x50 | ; var int local_51h @ ebp-0x51 | ; var int local_52h @ ebp-0x52 | ; var int local_53h @ ebp-0x53 | ; var int local_54h @ ebp-0x54 | ; CALL XREF from 0x08048ad9 (sym.main) | 0x0804903b 55 push ebp | 0x0804903c 89e5 mov ebp, esp | 0x0804903e 53 push ebx | 0x0804903f 83ec54 sub esp, 0x54 ; 'T' | 0x08049042 c645ac0e mov byte [local_54h], 0xe | 0x08049046 c645ad45 mov byte [local_53h], 0x45 ; 'E' | 0x0804904a c645ae9c mov byte [local_52h], 0x9c | 0x0804904e c645af33 mov byte [local_51h], 0x33 ; '3' | 0x08049052 c645b098 mov byte [local_50h], 0x98 | 0x08049056 c645b1af mov byte [local_4fh], 0xaf | 0x0804905a c645b249 mov byte [local_4eh], 0x49 ; 'I' | 0x0804905e c645b328 mov byte [local_4dh], 0x28 ; '(' | 0x08049062 c645b41c mov byte [local_4ch], 0x1c | 0x08049066 c645b503 mov byte [local_4bh], 3 | 0x0804906a c645b603 mov byte [local_4ah], 3 | 0x0804906e c645b762 mov byte [local_49h], 0x62 ; 'b' | 0x08049072 c645b8ee mov byte [local_48h], 0xee | 0x08049076 c645b9b7 mov byte [local_47h], 0xb7 | 0x0804907a c645ba87 mov byte [local_46h], 0x87 | 0x0804907e c645bb20 mov byte [local_45h], 0x20 | 0x08049082 c645bc90 mov byte [local_44h], 0x90 | 0x08049086 c645bd41 mov byte [local_43h], 0x41 ; 'A' | 0x0804908a c645be8e mov byte [local_42h], 0x8e | 0x0804908e c645bf44 mov byte [local_41h], 0x44 ; 'D' | 0x08049092 c645c0e1 mov byte [local_40h], 0xe1 | 0x08049096 c645c1ed mov byte [local_3fh], 0xed | 0x0804909a c645c24e mov byte [local_3eh], 0x4e ; 'N' | 0x0804909e c645c308 mov byte [local_3dh], 8 | 0x080490a2 c645c448 mov byte [local_3ch], 0x48 ; 'H' | 0x080490a6 c645c5ed mov byte [local_3bh], 0xed | 0x080490aa c645c6e6 mov byte [local_3ah], 0xe6 | 0x080490ae c645c71a mov byte [local_39h], 0x1a | 0x080490b2 c645c828 mov byte [local_38h], 0x28 ; '(' | 0x080490b6 c645c948 mov byte [local_37h], 0x48 ; 'H' | 0x080490ba c645cab6 mov byte [local_36h], 0xb6 | 0x080490be c645cb37 mov byte [local_35h], 0x37 ; '7' | 0x080490c2 c645ccac mov byte [local_34h], 0xac | 0x080490c6 c645cd33 mov byte [local_33h], 0x33 ; '3' | 0x080490ca c645ce95 mov byte [local_32h], 0x95 | 0x080490ce c645cf07 mov byte [local_31h], 7 | 0x080490d2 c745f0000000. mov dword [local_10h], 0 | 0x080490d9 c745f4000000. mov dword [local_ch], 0 | ,=< 0x080490e0 eb17 jmp 0x80490f9 | | ; JMP XREF from 0x080490fd (sym.check_flag_3) | .--> 0x080490e2 83ec08 sub esp, 8 | || 0x080490e5 6827900408 push sym.trap ; 0x8049027 ; "U\x89\xe5\xa1\xb4\xb1\x04\b\x05\x17O\x9d\x16\xa3\xb4\xb1\x04\b]\xc3U\x89\xe5S\x83\xecT\xc6E\xac\x0e\xc6E\xadE\xc6E\xae\x9c\xc6E\xaf3\xc6E\xb0\x98\xc6E\xb1\xaf\xc6E\xb2I\xc6E\xb3(\xc6E\xb4\x1c\xc6E\xb5\x03\xc6E\xb6\x03\xc6E\xb7b\xc6E\xb8\xee\xc6E\xb9\xb7\xc6E\xba\x87\xc6E\xbb \xc6E\xbc\x90\xc6E\xbdA\xc6E\xbe\x8e\xc6E\xbfD\xc6E\xc0\xe1\xc6E\xc1\xed\xc6E\xc2N\xc6E\xc3\b\xc6E\xc4H\xc6E\xc5\xed\xc6E\xc6\xe6\xc6E\xc7\x1a\xc6E\xc8(\xc6E\xc9H\xc6E\xca\xb6\xc6E\xcb7\xc6E\xcc\xac\xc6E\xcd3\xc6E\xce\x95\xc6E\xcf\x07\xc7E\xf0" | || 0x080490ea 6a05 push 5 | || 0x080490ec e81ff6ffff call sym.imp.__sysv_signal ; void signal(int sig, void *func) | || 0x080490f1 83c410 add esp, 0x10 | || 0x080490f4 cc int3 || 0x080490f5 8345f401 add dword [ebp - 0xc], 1 | !| ; JMP XREF from 0x080490e0 (sym.check_flag_3) | |`-> 0x080490f9 837df405 cmp dword [local_ch], 5 ; [0x5:4]=257 | `==< 0x080490fd 7ee3 jle 0x80490e2 | 0x080490ff 83ec08 sub esp, 8 | 0x08049102 6a18 push 0x18 ; " \x88\x04\b4" | 0x08049104 6a01 push 1 | 0x08049106 e805f7ffff call sym.imp.calloc ; void *calloc(size_t nmeb, size_t size) | 0x0804910b 83c410 add esp, 0x10 | 0x0804910e 8945ec mov dword [local_14h], eax | 0x08049111 837dec00 cmp dword [local_14h], 0 | ,=< 0x08049115 752b jne 0x8049142 | | 0x08049117 e864f6ffff call sym.imp.__errno_location | | 0x0804911c 8b00 mov eax, dword [eax] | | 0x0804911e c745e4ffffff. mov dword [local_1ch], 0xffffffff | | 0x08049125 8945e0 mov dword [local_20h], eax | | 0x08049128 c745dc9fa604. mov dword [local_24h], 0x804a69f | | 0x0804912f 6a18 push 0x18 ; " \x88\x04\b4" | | 0x08049131 ff75dc push dword [local_24h] | | 0x08049134 ff75e0 push dword [local_20h] | | 0x08049137 ff75e4 push dword [local_1ch] | | 0x0804913a e8b1f5ffff call sym.imp.error | | 0x0804913f 83c410 add esp, 0x10 | | ; JMP XREF from 0x08049115 (sym.check_flag_3) | `-> 0x08049142 8b45ec mov eax, dword [local_14h] | 0x08049145 c70024000000 mov dword [eax], 0x24 ; '$' ; [0x24:4]=0 ; '$' | 0x0804914b 8b15b4b10408 mov edx, dword obj.elder_sign ; [0x804b1b4:4]=0x392e3420 ; " 4.9.2-10) 4.9.2" | 0x08049151 8b45ec mov eax, dword [local_14h] | 0x08049154 895010 mov dword [eax + 0x10], edx | 0x08049157 8b45ec mov eax, dword [local_14h] | 0x0804915a 8d55ac lea edx, [local_54h] | 0x0804915d 89500c mov dword [eax + 0xc], edx | 0x08049160 8b45ec mov eax, dword [local_14h] | 0x08049163 8b5508 mov edx, dword [arg_8h] ; [0x8:4]=0 | 0x08049166 895004 mov dword [eax + 4], edx | 0x08049169 83ec0c sub esp, 0xc | 0x0804916c ff75ec push dword [local_14h] | 0x0804916f e84c0c0000 call sym.xorscura_compare | 0x08049174 83c410 add esp, 0x10 | 0x08049177 8945e8 mov dword [local_18h], eax | 0x0804917a 837de8ff cmp dword [local_18h], 0xffffffffffffffff | ,=< 0x0804917e 752d jne 0x80491ad | | 0x08049180 8b5dec mov ebx, dword [local_14h] | | 0x08049183 e8f8f5ffff call sym.imp.__errno_location | | 0x08049188 8b00 mov eax, dword [eax] | | 0x0804918a c745d8ffffff. mov dword [local_28h], 0xffffffff | | 0x08049191 8945d4 mov dword [local_2ch], eax | | 0x08049194 c745d0ada604. mov dword [local_30h], 0x804a6ad | | 0x0804919b 53 push ebx | | 0x0804919c ff75d0 push dword [local_30h] | | 0x0804919f ff75d4 push dword [local_2ch] | | 0x080491a2 ff75d8 push dword [local_28h] | | 0x080491a5 e846f5ffff call sym.imp.error | | 0x080491aa 83c410 add esp, 0x10 | | ; JMP XREF from 0x0804917e (sym.check_flag_3) | `-> 0x080491ad 837de800 cmp dword [local_18h], 0 | ,=< 0x080491b1 7507 jne 0x80491ba | | 0x080491b3 c745f0010000. mov dword [local_10h], 1 | | ; JMP XREF from 0x080491b1 (sym.check_flag_3) | `-> 0x080491ba 83ec0c sub esp, 0xc | 0x080491bd ff75ec push dword [local_14h] | 0x080491c0 e88b0c0000 call sym.xorscura_free_xod ; void free(void *ptr) | 0x080491c5 83c410 add esp, 0x10 | 0x080491c8 83ec0c sub esp, 0xc | 0x080491cb ff75ec push dword [local_14h] | 0x080491ce e8adf4ffff call sym.imp.free ; void free(void *ptr) | 0x080491d3 83c410 add esp, 0x10 | 0x080491d6 8b45f0 mov eax, dword [local_10h] | 0x080491d9 8b5dfc mov ebx, dword [local_4h] | 0x080491dc c9 leave \ 0x080491dd c3 ret
This uses signal()
, which I'm not very familiar with either. A quick jaunt over to man 2 signal
suggests that signal()
will be called with a signal number, and a handler - some a function pointer. This makes some sense given radare is telling us that the arguments to signal()
would be 5
and sym.trap
, an address of the function in this binary named trap
.
Additional interesting bits are the facts that signal()
is called in a loop, and that signal()
is immediately followed by an int3
, which signals a breakpoint for an attached debugger.
So, what's special about 5
? /usr/include/asm-generic/signal.h
defines signal numbers for us, showing 5
is the number for SIGTRAP
. man 7 signal
further informs us that SIGTRAP
is a signal for Trace/breakpoint trap
.
At this point the trick gets clearer: signal()
is being used to set a handler on SIGTRAP, but if a debugger is attached it will get the breakpoint signal first, and probably not duplicate that along to the debugged process out of the box, so the handler (trap()
) will never get called. Looks like we'll have to see what trap()
itself does.
/ (fcn) sym.trap 20 | sym.trap (); | ; DATA XREF from 0x080490e5 (sym.check_flag_3) | 0x08049027 55 push ebp ; .//flag_2.c:83 | 0x08049028 89e5 mov ebp, esp | 0x0804902a a1b4b10408 mov eax, dword obj.elder_sign ; [0x804b1b4:4]=0x392e3420 ; " 4.9.2-10) 4.9.2" | 0x0804902f 05174f9d16 add eax, 0x169d4f17 | 0x08049034 a3b4b10408 mov dword obj.elder_sign, eax ; [0x804b1b4:4]=0x392e3420 ; " 4.9.2-10) 4.9.2" | 0x08049039 5d pop ebp \ 0x0804903a c3 ret
trap()
does nothing but load a global variable, elder_sign
, add to it, and store it back!
Skipping forward in check_flag_3
a little, we can see elder_sign
is used before calling xorscura_compare()
, so trap
being called the correct number of times for it all to line up is probably important.
I'd look into trying to send SIGTRAP to the debugee and scripting that in gdb, but it seems simpler to patch the binary again to behave in a nicer way. Instead of calling trap()
in a roundabout way through signal handlers, let's patch the loop body to just.. call trap()
. So that's exactly what I did! At 0x10e2 in the binary, I nop'd out until 0x10f5 with 0x90 to clear some space. I stopped writing out nops there because we'd overwrite the add dword [ebp - 0xc], 1
that eventually trips the loop exit condition, which we want to preserve. Afterward, I wrote in call sym.trap
at 0x10e2. Beause call is an instruction with a four byte offset, we need to figure out the relative distance to trap()
from 0x80490ea. This comes out to 0xffffff38, for the whole instruction being e838ffffff
. Writing that into the binary at 0x10ea should preserve the "call trap() once each loop" behavior, but not break under debugging.
xorscura_compare()
With that rudeness out of the way, we can move on with debugging this program, which gets into xorscura_compare()
, a function we hadn't seen until now.
/ (fcn) sym.xorscura_compare 454 | sym.xorscura_compare (int arg_148h); | ! ; var int local_ch @ esp+0xc | ! ; var int local_14h @ esp+0x14 | ! ; var int local_18h @ esp+0x18 | ! ; var int local_1ch @ esp+0x1c | ! ; var int local_1dh @ esp+0x1d | ! ; var int local_1eh @ esp+0x1e | ! ; var int local_1fh @ esp+0x1f | ! ; var int local_20h @ esp+0x20 | ! ; var int local_24h @ esp+0x24 | ! ; arg int arg_148h @ esp+0x148 | ! ; CALL XREF from 0x0804916f (sym.check_flag_3) | ! ; CALL XREF from 0x08049528 (sym.check_flag_4) | ! ; CALL XREF from 0x080497ee (sym.check_flag_5) | | 0x08049dc0 55 push ebp | | 0x08049dc1 57 push edi | | 0x08049dc2 56 push esi | | 0x08049dc3 53 push ebx | | 0x08049dc4 8b442414 mov eax, dword [local_14h] ; [0x14:4]=1 | | 0x08049dc8 8b5010 mov edx, dword [eax + 0x10] ; [0x10:4]=0x30002 | | 0x08049dcb 85d2 test edx, edx | ,==< 0x08049dcd 7411 je 0x8049de0 | || 0x08049dcf 89442414 mov dword [local_14h], eax | || 0x08049dd3 5b pop ebx | || 0x08049dd4 5e pop esi | || 0x08049dd5 5f pop edi | || 0x08049dd6 5d pop ebp | |`=< 0x08049dd7 e984feffff jmp sym.xorscura_compare_prng | 0x08049ddc 8d742600 lea esi, [esi] | | ; JMP XREF from 0x08049dcd (sym.xorscura_compare) | `--> 0x08049de0 8b5808 mov ebx, dword [eax + 8] ; [0x8:4]=0 | 0x08049de3 85db test ebx, ebx | ,=< 0x08049de5 7453 je 0x8049e3a | | 0x08049de7 8b08 mov ecx, dword [eax] | | 0x08049de9 85c9 test ecx, ecx | ,==< 0x08049deb 7e3c jle 0x8049e29 | || 0x08049ded 8b700c mov esi, dword [eax + 0xc] ; [0xc:4]=0 | || 0x08049df0 8b7804 mov edi, dword [eax + 4] ; [0x4:4]=0x10101 | || 0x08049df3 0fb603 movzx eax, byte [ebx] | || 0x08049df6 3206 xor al, byte [esi] | || 0x08049df8 0fb617 movzx edx, byte [edi] | || 0x08049dfb 0fbec0 movsx eax, al | || 0x08049dfe 39c2 cmp edx, eax | ,===< 0x08049e00 752e jne 0x8049e30 | ||| 0x08049e02 31c0 xor eax, eax | ,====< 0x08049e04 eb1c jmp 0x8049e22 |||| 0x08049e06 8d7600 lea esi, [esi] |||| 0x08049e09 8dbc27000000. lea edi, [edi] | |||| ; JMP XREF from 0x08049e27 (sym.xorscura_compare) | .-----> 0x08049e10 0fb61403 movzx edx, byte [ebx + eax] | ||||| 0x08049e14 0fb62c07 movzx ebp, byte [edi + eax] | ||||| 0x08049e18 321406 xor dl, byte [esi + eax] | ||||| 0x08049e1b 0fbed2 movsx edx, dl | ||||| 0x08049e1e 39d5 cmp ebp, edx | ,======< 0x08049e20 750e jne 0x8049e30 | |!|||| ; JMP XREF from 0x08049e04 (sym.xorscura_compare) | ||`----> 0x08049e22 83c001 add eax, 1 | || ||| 0x08049e25 39c8 cmp eax, ecx | |`=====< 0x08049e27 75e7 jne 0x8049e10 | | ||| ; JMP XREF from 0x08049deb (sym.xorscura_compare) | | |`--> 0x08049e29 31c0 xor eax, eax | | | | ; JMP XREF from 0x08049e3f (sym.xorscura_compare) | | |.--> 0x08049e2b 5b pop ebx | | ||| 0x08049e2c 5e pop esi | | ||| 0x08049e2d 5f pop edi | | ||| 0x08049e2e 5d pop ebp | | ||| 0x08049e2f c3 ret | | |!| ; JMP XREF from 0x08049e00 (sym.xorscura_compare) | | |!| ; JMP XREF from 0x08049e20 (sym.xorscura_compare) | `--`---> 0x08049e30 5b pop ebx | || 0x08049e31 b801000000 mov eax, 1 | || 0x08049e36 5e pop esi | || 0x08049e37 5f pop edi | || 0x08049e38 5d pop ebp | || 0x08049e39 c3 ret | !| ; JMP XREF from 0x08049de5 (sym.xorscura_compare) | |`-> 0x08049e3a b8ffffffff mov eax, 0xffffffff ; -1 \ `==< 0x08049e3f ebea jmp 0x8049e2b
It looks like there are two main branches to this function, one of which is taken if a value in the struct passed in local_14h
is null, the other taken if it's set. If it's set, xorscura_compare_prng
is called, so I imagine that'll come into play for a later flag, with this value used in relation to the prng.
In the other case, which is what we see here, some xor
'ing and cmp
'ing is done. It's probably safe to say the flag is decoded and compared to user iput, so by putting a breakpoint on each cmp
after a xor
, we should be able to see flag bytes in registers.
There's an added complication, in that it bails out on the first mismatch. So let's patch a that out... the jne 0x8049e30
at 0x8049e00
has got to go. That bails if the first bytes don't match. At 0x1e00, I replaced that with 6690
, a two-byte nop. Now we have to give the same treatment to the jne 0x8049e30
at 0x8049e20
.
At this point, breakpoint'ing at 0x8049dfe
and 0x8049e1e
are sufficient to get us flag bytes, but we've accidentally made this function always return "true". That means flag 4
and flag 5
will be hard to get to. So let's fix that up by transmuting xor eax, eax
at 0x80489e29
into inc eax; inc eax
. That way it only returns 0 if eax
happened to be -2, which is... exceedingly unlikely. That one just replacing the 31c0
at 0x1e29
in the binary with 4040
.
And it is at exactly this point we may realize that for flag 3, we actually follow the branch to xorscura_compare_prng()
. Don't worry, the above patching comes in handy for later flags.
xorscura_compare_prng()
/ (fcn) sym.xorscura_compare_prng 308 | sym.xorscura_compare_prng (); | ; var int local_ch @ esp+0xc | ; var int local_18h @ esp+0x18 | ; var int local_1ch @ esp+0x1c | ; var int local_1dh @ esp+0x1d | ; var int local_1eh @ esp+0x1e | ; var int local_1fh @ esp+0x1f | ; var int local_20h @ esp+0x20 | ; var int local_24h @ esp+0x24 | ; var int local_148h @ esp+0x148 | ; JMP XREF from 0x08049dd7 (sym.xorscura_compare) | 0x08049c60 55 push ebp | 0x08049c61 57 push edi | 0x08049c62 56 push esi | 0x08049c63 53 push ebx | 0x08049c64 81ec34010000 sub esp, 0x134 | 0x08049c6a 8bb424480100. mov esi, dword [local_148h] ; [0x148:4]=4 | 0x08049c71 6a1c push 0x1c ; "4" | 0x08049c73 6a01 push 1 | 0x08049c75 e896ebffff call sym.imp.calloc ; void *calloc(size_t nmeb, size_t size) | 0x08049c7a 8944241c mov dword [local_1ch], eax | 0x08049c7e 83c410 add esp, 0x10 | 0x08049c81 85c0 test eax, eax | ,=< 0x08049c83 0f8427010000 je 0x8049db0 | | 0x08049c89 8d542420 lea edx, [local_20h] ; 0x20 | | 0x08049c8d 31c0 xor eax, eax | | 0x08049c8f b940000000 mov ecx, 0x40 ; '@' ; "4\x80\x04\b" | | 0x08049c94 89d7 mov edi, edx | | 0x08049c96 f3ab rep stosd dword es:[edi], eax | | 0x08049c98 ff74240c push dword [local_ch] | | 0x08049c9c 6800010000 push 0x100 | | 0x08049ca1 52 push edx | | 0x08049ca2 ff7610 push dword [esi + 0x10] | | 0x08049ca5 e856ebffff call sym.imp.initstate_r | | 0x08049caa 83c410 add esp, 0x10 | | 0x08049cad 83f8ff cmp eax, 0xffffffffffffffff | ,==< 0x08049cb0 0f84fa000000 je 0x8049db0 | || 0x08049cb6 8b2e mov ebp, dword [esi] | || 0x08049cb8 31db xor ebx, ebx | || ; JMP XREF from 0x08049da2 (sym.xorscura_compare_prng) | .---> 0x08049cba 39d3 cmp ebx, edx | ,====< 0x08049cbc 0f839f000000 jae 0x8049d61 | |!|| ; JMP XREF from 0x08049d5b (sym.xorscura_compare_prng) | .-----> 0x08049cc2 83ec08 sub esp, 8 | ||||| 0x08049cc5 8d442424 lea eax, [local_24h] ; 0x24 ; '$' | ||||| 0x08049cc9 50 push eax | ||||| 0x08049cca ff742418 push dword [local_18h] | ||||| 0x08049cce e8ddeaffff call sym.imp.random_r ; int rand(void) | ||||| 0x08049cd3 83c410 add esp, 0x10 | ||||| 0x08049cd6 83f8ff cmp eax, 0xffffffffffffffff | ,======< 0x08049cd9 0f84d1000000 je 0x8049db0 | |||||| 0x08049cdf 8b4e0c mov ecx, dword [esi + 0xc] ; [0xc:4]=0 | |||||| 0x08049ce2 8b7e0c mov edi, dword [esi + 0xc] ; [0xc:4]=0 | |||||| 0x08049ce5 0fb6041f movzx eax, byte [edi + ebx] | |||||| 0x08049ce9 3244241c xor al, byte [local_1ch] | |||||| 0x08049ced 666690 nop | |||||| 0x08049cf0 50 push eax | |||||| 0x08049cf1 54 push esp | |||||| 0x08049cf2 e809eaffff call sym.imp.puts ; int puts(const char *s) | |||||| 0x08049cf7 58 pop eax | |||||| 0x08049cf8 58 pop eax | |||||| 0x08049cf9 666690 nop | |||||| 0x08049cfc 8b16 mov edx, dword [esi] | |||||| 0x08049cfe 8d4301 lea eax, [ebx + 1] | |||||| 0x08049d01 39c2 cmp edx, eax | ,=======< 0x08049d03 0f8497000000 je 0x8049da0 | ||||||| 0x08049d09 0fb6441f01 movzx eax, byte [edi + ebx + 1] ; [0x1:1]=69 | ||||||| 0x08049d0e 3244241d xor al, byte [local_1dh] | ||||||| 0x08049d12 666690 nop | ||||||| 0x08049d15 50 push eax | ||||||| 0x08049d16 54 push esp | ||||||| 0x08049d17 e8e4e9ffff call sym.imp.puts ; int puts(const char *s) | ||||||| 0x08049d1c 58 pop eax | ||||||| 0x08049d1d 58 pop eax | ||||||| 0x08049d1e 8d4302 lea eax, [ebx + 2] | ||||||| 0x08049d21 39c2 cmp edx, eax | ========< 0x08049d23 747b je 0x8049da0 | ||||||| 0x08049d25 0fb6441f02 movzx eax, byte [edi + ebx + 2] ; [0x2:1]=76 | ||||||| 0x08049d2a 3244241e xor al, byte [local_1eh] | ||||||| 0x08049d2e 666690 nop | ||||||| 0x08049d31 50 push eax | ||||||| 0x08049d32 54 push esp | ||||||| 0x08049d33 e8c8e9ffff call sym.imp.puts ; int puts(const char *s) | ||||||| 0x08049d38 58 pop eax | ||||||| 0x08049d39 58 pop eax | ||||||| 0x08049d3a 8d4303 lea eax, [ebx + 3] ; "F\x01\x01\x01" | ||||||| 0x08049d3d 39c2 cmp edx, eax | ========< 0x08049d3f 745f je 0x8049da0 | ||||||| 0x08049d41 0fb6441f03 movzx eax, byte [edi + ebx + 3] ; [0x3:1]=70 ; "F\x01\x01\x01" | ||||||| 0x08049d46 3244241f xor al, byte [local_1fh] | ||||||| 0x08049d4a 666690 nop | ||||||| 0x08049d4d 50 push eax | ||||||| 0x08049d4e 54 push esp | ||||||| 0x08049d4f e8ace9ffff call sym.imp.puts ; int puts(const char *s) | ||||||| 0x08049d54 58 pop eax | ||||||| 0x08049d55 58 pop eax | ||||||| 0x08049d56 83c304 add ebx, 4 | ||||||| 0x08049d59 39eb cmp ebx, ebp | ||`=====< 0x08049d5b 0f8261ffffff jb 0x8049cc2 | || |!|| ; JMP XREF from 0x08049cbc (sym.xorscura_compare_prng) | || `----> 0x08049d61 83ec0c sub esp, 0xc | || ||| 0x08049d64 ff742418 push dword [local_18h] | || ||| 0x08049d68 e813e9ffff call sym.imp.free ; void free(void *ptr) | || ||| 0x08049d6d 83c410 add esp, 0x10 | || ||| 0x08049d70 31c0 xor eax, eax | || ||| 0x08049d72 81c42c010000 add esp, 0x12c | || ||| 0x08049d78 5b pop ebx | || ||| 0x08049d79 5e pop esi | || ||| 0x08049d7a 5f pop edi | || ||| 0x08049d7b 5d pop ebp | || ||| 0x08049d7c c3 ret || ||| 0x08049d7d 8d7600 lea esi, [esi] || ||| 0x08049d80 83ec0c sub esp, 0xc || ||| 0x08049d83 ff742418 push dword [esp + 0x18] || ||| 0x08049d87 e8f4e8ffff call sym.imp.free ; void free(void *ptr) || ||| 0x08049d8c 83c410 add esp, 0x10 || ||| 0x08049d8f b801000000 mov eax, 1 || ||| 0x08049d94 81c42c010000 add esp, 0x12c || ||| 0x08049d9a 5b pop ebx || ||| 0x08049d9b 5e pop esi || ||| 0x08049d9c 5f pop edi || ||| 0x08049d9d 5d pop ebp || ||| 0x08049d9e c3 ret || ||| 0x08049d9f 90 nop | || !|| ; JMP XREF from 0x08049d03 (sym.xorscura_compare_prng) | || !|| ; JMP XREF from 0x08049d23 (sym.xorscura_compare_prng) | || !|| ; JMP XREF from 0x08049d3f (sym.xorscura_compare_prng) | `-------> 0x08049da0 89d3 mov ebx, edx | | `===< 0x08049da2 e913ffffff jmp 0x8049cba | || 0x08049da7 89f6 mov esi, esi | || 0x08049da9 8dbc27000000. lea edi, [edi] | | || ; JMP XREF from 0x08049c83 (sym.xorscura_compare_prng) | | || ; JMP XREF from 0x08049cb0 (sym.xorscura_compare_prng) | | || ; JMP XREF from 0x08049cd9 (sym.xorscura_compare_prng) | `---``-> 0x08049db0 81c42c010000 add esp, 0x12c | 0x08049db6 b8ffffffff mov eax, 0xffffffff ; -1 | 0x08049dbb 5b pop ebx | 0x08049dbc 5e pop esi | 0x08049dbd 5f pop edi | 0x08049dbe 5d pop ebp \ 0x08049dbf c3 ret
calloc()
0x1c bytesinitstate_r
with that struct- do some stuff and maybe
random_r
with that struct - after
random_r
, bytes are decoded one at a time, in groups of four, each compared to a corresponding byte of user input - if any decoded byte does not match up, return 1.
Much like in xorscura_compare()
, this will exit early if any bytes of the flag don't match, but it'd be nice for it to just print out all bytes of the flag.
nop'ing out conditional jumps at 0x8049cf6
, 0x8049d1c
, 0x8049d38
, and 0x8049d54
should do the trick, though we'll need to "fix" xor eax, eax
at 0x8049d70
just as before, where inc eax; inc eax
will do.
,==< 0x08049cd9 0f84d1000000 je 0x8049db0 || 0x08049cdf 8b4e0c mov ecx, dword [esi + 0xc] ; [0xc:4]=0 || 0x08049ce2 8b7e04 mov edi, dword [esi + 4] ; [0x4:4]=0x10101 || 0x08049ce5 0fb60419 movzx eax, byte [ecx + ebx] || 0x08049ce9 0fb6141f movzx edx, byte [edi + ebx] || 0x08049ced 3244241c xor al, byte [esp + 0x1c] || 0x08049cf1 0fbec0 movsx eax, al || 0x08049cf4 39c2 cmp edx, eax || 0x08049cf6 666666666690 nop || 0x08049cfc 8b16 mov edx, dword [esi] || 0x08049cfe 8d4301 lea eax, [ebx + 1] || 0x08049d01 39c2 cmp edx, eax ,===< 0x08049d03 0f8497000000 je 0x8049da0 ||| 0x08049d09 0fb6441901 movzx eax, byte [ecx + ebx + 1] ; [0x1:1]=69 ||| 0x08049d0e 0fb66c1f01 movzx ebp, byte [edi + ebx + 1] ; [0x1:1]=69 ||| 0x08049d13 3244241d xor al, byte [esp + 0x1d] ||| 0x08049d17 0fbec0 movsx eax, al ||| 0x08049d1a 39c5 cmp ebp, eax ||| 0x08049d1c 6690 nop ||| 0x08049d1e 8d4302 lea eax, [ebx + 2] ||| 0x08049d21 39c2 cmp edx, eax ,====< 0x08049d23 747b je 0x8049da0 |||| 0x08049d25 0fb6441902 movzx eax, byte [ecx + ebx + 2] ; [0x2:1]=76 |||| 0x08049d2a 0fb66c1f02 movzx ebp, byte [edi + ebx + 2] ; [0x2:1]=76 |||| 0x08049d2f 3244241e xor al, byte [esp + 0x1e] |||| 0x08049d33 0fbec0 movsx eax, al |||| 0x08049d36 39c5 cmp ebp, eax ,=====< 0x08049d38 7546 jne 0x8049d80 ||||| 0x08049d3a 8d4303 lea eax, [ebx + 3] ; "F\x01\x01\x01" ||||| 0x08049d3d 39c2 cmp edx, eax ,======< 0x08049d3f 745f je 0x8049da0 |||||| 0x08049d41 0fb6441903 movzx eax, byte [ecx + ebx + 3] ; [0x3:1]=70 ; "F\x01\x01\x01" |||||| 0x08049d46 0fb67c1f03 movzx edi, byte [edi + ebx + 3] ; [0x3:1]=70 ; "F\x01\x01\x01" |||||| 0x08049d4b 3244241f xor al, byte [esp + 0x1f] |||||| 0x08049d4f 0fbec0 movsx eax, al |||||| 0x08049d52 39c7 cmp edi, eax ,=======< 0x08049d54 752a jne 0x8049d80 ||||||| 0x08049d56 83c304 add ebx, 4 ||||||| 0x08049d59 39d3 cmp ebx, edx ||||||`=< 0x08049d5b 0f8261ffffff jb 0x8049cc2 |||||| 0x08049d61 83ec0c sub esp, 0xc |||||| 0x08049d64 ff742418 push dword [esp + 0x18] |||||| 0x08049d68 e813e9ffff call sym.imp.free |||||| 0x08049d6d 83c410 add esp, 0x10 |||||| 0x08049d70 31c0 xor eax, eax |||||| 0x08049d72 81c42c010000 add esp, 0x12c |||||| 0x08049d78 5b pop ebx |||||| 0x08049d79 5e pop esi |||||| 0x08049d7a 5f pop edi |||||| 0x08049d7b 5d pop ebp |||||| 0x08049d7c c3 ret
Now with breakpoints at 0x8049cf4
, 0x8049d1a
, 0x8049d36
, and 0x8049d52
and display/c $eax
we'll get a flag one byte at a time when run and debugged!
+300
Flag 4
/ (fcn) sym.check_flag_4 751 | sym.check_flag_4 (); | ; var int local_8h @ ebp-0x8 | ; var int local_58h @ ebp-0x58 | ; var int local_54h @ ebp-0x54 | ; var int local_50h @ ebp-0x50 | ; var int local_30h @ ebp-0x30 | ; var int local_2ch @ ebp-0x2c | ; var int local_40h @ ebp-0x40 | ; var int local_3ch @ ebp-0x3c | ; var int local_38h @ ebp-0x38 | ; var int local_28h @ ebp-0x28 | ; var int local_1ch @ ebp-0x1c | ; var int local_24h @ ebp-0x24 | ; var int local_9dh @ ebp-0x9d | ; var int local_9eh @ ebp-0x9e | ; var int local_9fh @ ebp-0x9f | ; var int local_a0h @ ebp-0xa0 | ; var int local_a1h @ ebp-0xa1 | ; var int local_a2h @ ebp-0xa2 | ; var int local_a3h @ ebp-0xa3 | ; var int local_a4h @ ebp-0xa4 | ; var int local_a5h @ ebp-0xa5 | ; var int local_a6h @ ebp-0xa6 | ; var int local_a7h @ ebp-0xa7 | ; var int local_a8h @ ebp-0xa8 | ; var int local_a9h @ ebp-0xa9 | ; var int local_aah @ ebp-0xaa | ; var int local_20h @ ebp-0x20 | ; var int local_79h @ ebp-0x79 | ; var int local_7ah @ ebp-0x7a | ; var int local_7bh @ ebp-0x7b | ; var int local_7ch @ ebp-0x7c | ; var int local_7dh @ ebp-0x7d | ; var int local_7eh @ ebp-0x7e | ; var int local_7fh @ ebp-0x7f | ; var int local_80h @ ebp-0x80 | ; var int local_81h @ ebp-0x81 | ; var int local_82h @ ebp-0x82 | ; var int local_83h @ ebp-0x83 | ; var int local_84h @ ebp-0x84 | ; var int local_85h @ ebp-0x85 | ; var int local_86h @ ebp-0x86 | ; var int local_87h @ ebp-0x87 | ; var int local_88h @ ebp-0x88 | ; var int local_89h @ ebp-0x89 | ; var int local_8ah @ ebp-0x8a | ; var int local_8bh @ ebp-0x8b | ; var int local_8ch @ ebp-0x8c | ; var int local_8dh @ ebp-0x8d | ; var int local_8eh @ ebp-0x8e | ; var int local_8fh @ ebp-0x8f | ; var int local_90h @ ebp-0x90 | ; var int local_91h @ ebp-0x91 | ; var int local_92h @ ebp-0x92 | ; var int local_93h @ ebp-0x93 | ; var int local_94h @ ebp-0x94 | ; var int local_95h @ ebp-0x95 | ; var int local_96h @ ebp-0x96 | ; var int local_97h @ ebp-0x97 | ; var int local_98h @ ebp-0x98 | ; var int local_99h @ ebp-0x99 | ; var int local_9ah @ ebp-0x9a | ; var int local_9bh @ ebp-0x9b | ; var int local_9ch @ ebp-0x9c | ; var int local_78h @ ebp-0x78 | ; var int local_4h @ esp+0x4 | ; CALL XREF from 0x08048b04 (sym.main) | 0x080491de 8d4c2404 lea ecx, [local_4h] | 0x080491e2 83e4e0 and esp, 0xffffffe0 | 0x080491e5 ff71fc push dword [ecx - 4] | 0x080491e8 55 push ebp | 0x080491e9 89e5 mov ebp, esp | 0x080491eb 53 push ebx | 0x080491ec 51 push ecx | 0x080491ed 81ecb0000000 sub esp, 0xb0 | 0x080491f3 89cb mov ebx, ecx | 0x080491f5 c74588ed0508. mov dword [local_78h], 0x570805ed | 0x080491fc c68564ffffff. mov byte [local_9ch], 0xd3 | 0x08049203 c68565ffffff. mov byte [local_9bh], 0x67 ; 'g' | 0x0804920a c68566ffffff. mov byte [local_9ah], 0x3d ; '=' | 0x08049211 c68567ffffff. mov byte [local_99h], 0x33 ; '3' | 0x08049218 c68568ffffff. mov byte [local_98h], 0x2c ; ',' | 0x0804921f c68569ffffff. mov byte [local_97h], 0x48 ; 'H' | 0x08049226 c6856affffff. mov byte [local_96h], 0x39 ; '9' | 0x0804922d c6856bffffff. mov byte [local_95h], 0x20 | 0x08049234 c6856cffffff. mov byte [local_94h], 0xad | 0x0804923b c6856dffffff. mov byte [local_93h], 0xcf | 0x08049242 c6856effffff. mov byte [local_92h], 0xd | 0x08049249 c6856fffffff. mov byte [local_91h], 0xb | 0x08049250 c68570ffffff. mov byte [local_90h], 0xab | 0x08049257 c68571ffffff. mov byte [local_8fh], 0x2d ; '-' | 0x0804925e c68572ffffff. mov byte [local_8eh], 0xbe | 0x08049265 c68573ffffff. mov byte [local_8dh], 0x11 | 0x0804926c c68574ffffff. mov byte [local_8ch], 0x1a | 0x08049273 c68575ffffff. mov byte [local_8bh], 0xc3 | 0x0804927a c68576ffffff. mov byte [local_8ah], 0x6a ; 'j' | 0x08049281 c68577ffffff. mov byte [local_89h], 0xe | 0x08049288 c68578ffffff. mov byte [local_88h], 0x68 ; 'h' | 0x0804928f c68579ffffff. mov byte [local_87h], 0xa5 | 0x08049296 c6857affffff. mov byte [local_86h], 0x97 | 0x0804929d c6857bffffff. mov byte [local_85h], 0x4d ; 'M' | 0x080492a4 c6857cffffff. mov byte [local_84h], 0xa1 | 0x080492ab c6857dffffff. mov byte [local_83h], 5 | 0x080492b2 c6857effffff. mov byte [local_82h], 0x45 ; 'E' | 0x080492b9 c6857fffffff. mov byte [local_81h], 0x49 ; 'I' | 0x080492c0 c6458039 mov byte [local_80h], 0x39 ; '9' | 0x080492c4 c645817b mov byte [local_7fh], 0x7b ; '{' | 0x080492c8 c64582c7 mov byte [local_7eh], 0xc7 | 0x080492cc c6458367 mov byte [local_7dh], 0x67 ; 'g' | 0x080492d0 c6458418 mov byte [local_7ch], 0x18 | 0x080492d4 c6458515 mov byte [local_7bh], 0x15 | 0x080492d8 c6458646 mov byte [local_7ah], 0x46 ; 'F' | 0x080492dc c645875b mov byte [local_79h], 0x5b ; '[' | 0x080492e0 c745e0d03574. mov dword [local_20h], 0x1a7435d0 | 0x080492e7 c68556ffffff. mov byte [local_aah], 0xab | 0x080492ee c68557ffffff. mov byte [local_a9h], 0xeb | 0x080492f5 c68558ffffff. mov byte [local_a8h], 0x77 ; 'w' | 0x080492fc c68559ffffff. mov byte [local_a7h], 0x6f ; 'o' | 0x08049303 c6855affffff. mov byte [local_a6h], 0xe9 | 0x0804930a c6855bffffff. mov byte [local_a5h], 0xd5 | 0x08049311 c6855cffffff. mov byte [local_a4h], 0x97 | 0x08049318 c6855dffffff. mov byte [local_a3h], 0x2a ; '*' | 0x0804931f c6855effffff. mov byte [local_a2h], 0x3c ; '<' | 0x08049326 c6855fffffff. mov byte [local_a1h], 0xed | 0x0804932d c68560ffffff. mov byte [local_a0h], 0x7a ; 'z' | 0x08049334 c68561ffffff. mov byte [local_9fh], 0xc | 0x0804933b c68562ffffff. mov byte [local_9eh], 0xf6 | 0x08049342 c68563ffffff. mov byte [local_9dh], 0xa8 | 0x08049349 c745dc0e0000. mov dword [local_24h], 0xe | 0x08049350 c745e4000000. mov dword [local_1ch], 0 | 0x08049357 83ec08 sub esp, 8 | 0x0804935a 6a18 push 0x18 ; " \x88\x04\b4" | 0x0804935c 6a01 push 1 ; "ELF\x01\x01\x01" | 0x0804935e e8adf4ffff call sym.imp.calloc ; void *calloc(size_t nmeb, size_t size) | 0x08049363 83c410 add esp, 0x10 | 0x08049366 8945d8 mov dword [local_28h], eax | 0x08049369 837dd800 cmp dword [local_28h], 0 | ,=< 0x0804936d 752b jne 0x804939a | | 0x0804936f e80cf4ffff call sym.imp.__errno_location | | 0x08049374 8b00 mov eax, dword [eax] | | 0x08049376 c745c8ffffff. mov dword [local_38h], 0xffffffff | | 0x0804937d 8945c4 mov dword [local_3ch], eax | | 0x08049380 c745c0c3a604. mov dword [local_40h], 0x804a6c3 | | 0x08049387 6a18 push 0x18 ; " \x88\x04\b4" | | 0x08049389 ff75c0 push dword [local_40h] | | 0x0804938c ff75c4 push dword [local_3ch] | | 0x0804938f ff75c8 push dword [local_38h] | | 0x08049392 e859f3ffff call sym.imp.error | | 0x08049397 83c410 add esp, 0x10 | | ; JMP XREF from 0x0804936d (sym.check_flag_4) | `-> 0x0804939a e8f1f3ffff call sym.imp.fork | 0x0804939f 8945d4 mov dword [local_2ch], eax | 0x080493a2 837dd400 cmp dword [local_2ch], 0 | ,=< 0x080493a6 0f85e0000000 jne 0x804948c | | 0x080493ac 6a00 push 0 | | 0x080493ae 6a00 push 0 | | 0x080493b0 6a00 push 0 | | 0x080493b2 6a00 push 0 | | 0x080493b4 e827f4ffff call sym.imp.ptrace | | 0x080493b9 83c410 add esp, 0x10 | | 0x080493bc cc int3 | 0x080493bd 8b45d8 mov eax, dword [ebp - 0x28] | 0x080493c0 8b55dc mov edx, dword [ebp - 0x24] | 0x080493c3 8910 mov dword [eax], edx | 0x080493c5 8b45d8 mov eax, dword [ebp - 0x28] | 0x080493c8 8b55e0 mov edx, dword [ebp - 0x20] | 0x080493cb 895010 mov dword [eax + 0x10], edx | 0x080493ce 8b45d8 mov eax, dword [ebp - 0x28] | 0x080493d1 8d9556ffffff lea edx, [ebp - 0xaa] | 0x080493d7 89500c mov dword [eax + 0xc], edx | 0x080493da 83ec0c sub esp, 0xc | 0x080493dd ff75d8 push dword [ebp - 0x28] | 0x080493e0 e8fb070000 call sym.xorscura_decrypt | 0x080493e5 83c410 add esp, 0x10 | 0x080493e8 83f8ff cmp eax, 0xffffffffffffffff ,==< 0x080493eb 752d jne 0x804941a || 0x080493ed 8b5dd8 mov ebx, dword [ebp - 0x28] || 0x080493f0 e88bf3ffff call sym.imp.__errno_location || 0x080493f5 8b00 mov eax, dword [eax] || 0x080493f7 c745bcffffff. mov dword [ebp - 0x44], 0xffffffff || 0x080493fe 8945b8 mov dword [ebp - 0x48], eax || 0x08049401 c745b4d1a604. mov dword [ebp - 0x4c], str.xorscura_decrypt__lx_ || 0x08049408 53 push ebx || 0x08049409 ff75b4 push dword [ebp - 0x4c] || 0x0804940c ff75b8 push dword [ebp - 0x48] || 0x0804940f ff75bc push dword [ebp - 0x44] || 0x08049412 e8d9f2ffff call sym.imp.error || 0x08049417 83c410 add esp, 0x10 || ; JMP XREF from 0x080493eb (sym.check_flag_4 + 525) `--> 0x0804941a 8b45d8 mov eax, dword [ebp - 0x28] | 0x0804941d 8b4004 mov eax, dword [eax + 4] ; [0x4:4]=0x10101 | 0x08049420 83ec08 sub esp, 8 | 0x08049423 6a00 push 0 | 0x08049425 50 push eax | 0x08049426 e815f3ffff call sym.imp.open ; int open(const char *path, int oflag) | 0x0804942b 83c410 add esp, 0x10 | 0x0804942e 8945cc mov dword [ebp - 0x34], eax | 0x08049431 83ec04 sub esp, 4 | 0x08049434 6a02 push 2 ; "LF\x01\x01\x01" | 0x08049436 6af5 push 0xfffffffffffffff5 | 0x08049438 ff75cc push dword [ebp - 0x34] | 0x0804943b e860f2ffff call sym.imp.lseek | 0x08049440 83c410 add esp, 0x10 | 0x08049443 83ec04 sub esp, 4 | 0x08049446 6a0b push 0xb | 0x08049448 8d854bffffff lea eax, [ebp - 0xb5] | 0x0804944e 50 push eax | 0x0804944f ff75cc push dword [ebp - 0x34] | 0x08049452 e809f2ffff call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte) | 0x08049457 83c410 add esp, 0x10 | 0x0804945a 83ec04 sub esp, 4 | 0x0804945d 6a0a push 0xa | 0x0804945f 6a00 push 0 | 0x08049461 8d854bffffff lea eax, [ebp - 0xb5] | 0x08049467 50 push eax | 0x08049468 e863f3ffff call sym.imp.strtol ; long strtol(const char *str, char**endptr, int base) | 0x0804946d 83c410 add esp, 0x10 | 0x08049470 894588 mov dword [ebp - 0x78], eax | 0x08049473 83ec0c sub esp, 0xc | 0x08049476 ff75cc push dword [ebp - 0x34] | 0x08049479 e872f3ffff call sym.imp.close ; int close(int fildes) | 0x0804947e 83c410 add esp, 0x10 | 0x08049481 cc int3 | 0x08049482 83ec0c sub esp, 0xc | 0x08049485 6a00 push 0 | 0x08049487 e8a4f2ffff call sym.imp.exit ; void exit(int status) | | ; JMP XREF from 0x080493a6 (sym.check_flag_4) | `-> 0x0804948c 83ec0c sub esp, 0xc | 0x0804948f 6a00 push 0 | 0x08049491 e82af2ffff call sym.imp.wait | 0x08049496 83c410 add esp, 0x10 | 0x08049499 6a00 push 0 | 0x0804949b 6a00 push 0 | 0x0804949d ff75d4 push dword [local_2ch] | 0x080494a0 6a10 push 0x10 | 0x080494a2 e839f3ffff call sym.imp.ptrace | 0x080494a7 83c410 add esp, 0x10 | 0x080494aa 6a00 push 0 | 0x080494ac 6a00 push 0 | 0x080494ae ff75d4 push dword [local_2ch] | 0x080494b1 6a07 push 7 | 0x080494b3 e828f3ffff call sym.imp.ptrace | 0x080494b8 83c410 add esp, 0x10 | 0x080494bb 83ec0c sub esp, 0xc | 0x080494be 6a00 push 0 | 0x080494c0 e8fbf1ffff call sym.imp.wait | 0x080494c5 83c410 add esp, 0x10 | 0x080494c8 6a00 push 0 | 0x080494ca 8d4588 lea eax, [local_78h] | 0x080494cd 50 push eax | 0x080494ce ff75d4 push dword [local_2ch] | 0x080494d1 6a02 push 2 ; "LF\x01\x01\x01" | 0x080494d3 e808f3ffff call sym.imp.ptrace | 0x080494d8 83c410 add esp, 0x10 | 0x080494db 894588 mov dword [local_78h], eax | 0x080494de 6a00 push 0 | 0x080494e0 6a00 push 0 | 0x080494e2 ff75d4 push dword [local_2ch] | 0x080494e5 6a11 push 0x11 | 0x080494e7 e8f4f2ffff call sym.imp.ptrace | 0x080494ec 83c410 add esp, 0x10 | 0x080494ef 83ec0c sub esp, 0xc | 0x080494f2 6a00 push 0 | 0x080494f4 e8c7f1ffff call sym.imp.wait | 0x080494f9 83c410 add esp, 0x10 | 0x080494fc 8b45d8 mov eax, dword [local_28h] | 0x080494ff c70024000000 mov dword [eax], 0x24 ; '$' ; [0x24:4]=0 ; '$' | 0x08049505 8b5588 mov edx, dword [local_78h] | 0x08049508 8b45d8 mov eax, dword [local_28h] | 0x0804950b 895010 mov dword [eax + 0x10], edx | 0x0804950e 8b45d8 mov eax, dword [local_28h] | 0x08049511 8d9564ffffff lea edx, [local_9ch] | 0x08049517 89500c mov dword [eax + 0xc], edx | 0x0804951a 8b45d8 mov eax, dword [local_28h] | 0x0804951d 8b13 mov edx, dword [ebx] | 0x0804951f 895004 mov dword [eax + 4], edx | 0x08049522 83ec0c sub esp, 0xc | 0x08049525 ff75d8 push dword [local_28h] | 0x08049528 e893080000 call sym.xorscura_compare | 0x0804952d 83c410 add esp, 0x10 | 0x08049530 8945d0 mov dword [local_30h], eax | 0x08049533 837dd0ff cmp dword [local_30h], 0xffffffffffffffff | ,=< 0x08049537 752d jne 0x8049566 | | 0x08049539 8b5dd8 mov ebx, dword [local_28h] | | 0x0804953c e83ff2ffff call sym.imp.__errno_location | | 0x08049541 8b00 mov eax, dword [eax] | | 0x08049543 c745b0ffffff. mov dword [local_50h], 0xffffffff | | 0x0804954a 8945ac mov dword [local_54h], eax | | 0x0804954d c745a8e7a604. mov dword [local_58h], 0x804a6e7 | | 0x08049554 53 push ebx | | 0x08049555 ff75a8 push dword [local_58h] | | 0x08049558 ff75ac push dword [local_54h] | | 0x0804955b ff75b0 push dword [local_50h] | | 0x0804955e e88df1ffff call sym.imp.error | | 0x08049563 83c410 add esp, 0x10 | | ; JMP XREF from 0x08049537 (sym.check_flag_4) | `-> 0x08049566 837dd000 cmp dword [local_30h], 0 | ,=< 0x0804956a 7507 jne 0x8049573 | | 0x0804956c c745e4010000. mov dword [local_1ch], 1 | | ; JMP XREF from 0x0804956a (sym.check_flag_4) | `-> 0x08049573 83ec0c sub esp, 0xc | 0x08049576 ff75d8 push dword [local_28h] | 0x08049579 e8d2080000 call sym.xorscura_free_xod ; void free(void *ptr) | 0x0804957e 83c410 add esp, 0x10 | 0x08049581 83ec0c sub esp, 0xc | 0x08049584 ff75d8 push dword [local_28h] | 0x08049587 e8f4f0ffff call sym.imp.free ; void free(void *ptr) | 0x0804958c 83c410 add esp, 0x10 | 0x0804958f 8b45e4 mov eax, dword [local_1ch] | 0x08049592 8d65f8 lea esp, [local_8h] | 0x08049595 59 pop ecx | 0x08049596 5b pop ebx | 0x08049597 5d pop ebp | 0x08049598 8d61fc lea esp, [ecx - 4] \ 0x0804959b c3 ret
This function just looks annoying, what with the opens and ptrace()
and all this.. stuff going on. Also the fork()
, meaning I'd have to learn how to have gdb debug child processes as well. All of that sounds annoying, but here's an idea: this still calls xorscura_compare()
. And, it turns out check_flag_5
also calls xorscura_compare()
. So let's put this fork/ptrace business aside a moment and look at that.
Now, avoiding the anti-debug tricks is annoying. Wouldn't it be easier if the program just printed out the flag bytes?
How could we make that happen?
puts()
to the rescue
I noticed earlier that puts()
is used in main()
, and it has a dead-simple interface. Pass it a pointer, it prints characters until it reaches a null.
Flags here are available 1-4 bytes at a time, but tinkering with imports to get putchar
exposed is absolutely not how I wanted to spend time - puts()
'ing the flag one byte at a time is plenty good for CTF.
How can we cram a puts()
call in though? Calls are large (5 byte) instructions, and we only have a few bytes to work with from the cmp
and corresponding jne
.
.. Or do we? If we assume inputs are all multiples of 4 (which they are, because flags are multiples of 4 and we'll just type in the right stuff), we can remove all the lea eax, [ebx + N]; cmp edx, eax; je 0x8049da0
. That lets us go from 4 bytes to 11 bytes to work with. We can even discard the user input string entirely and consider movzx ebp, byte [edi + ebx + 1]
space up for grabs too. That puts us at 16 bytes of code space to mess with for each byte of the flag we want to print, with four bytes we want to print.
no matter what we need to be careful to not clobber registers
So what registers are preserved by the ABI? That is, what do we have to worry about being trashed by putting in a random puts()
or four?
ebx
, esi
, edi
, ebp
, and esp
At each of these xor
, edi
, esi
, edx
, ecx
, ebx
, and eax
are used.
So we have to worry about edx
, ecx
, and eax
. Push/pop are one byte each, call is five, for a total of 11 bytes if we can arrange the stack right. This isn't quite small enough.
Maybe we can move one of these values into a register that won't be trashed by puts()
? We won't be using edi
anymore, which was used for the user-input flag string. So let's patch the mov edx, dword [esi]
into a mov edi, dword [esi]
instead, saving us two bytes in future patches, making this insanity workable! In addition, we'll need to patch cmp
at 0x8049d01
, 0x8049d21
, 0x8049d3d
, and 0x8049d59
to all use edi
instead of edx
. Respectively, they change to 39c7
, 39c7
, 39c7
, and 39fd
. The mov
at 0x8049cfc
also will turn into 8b3e
.
For some reason, the compiler decided to put the last value of each loop in edi
instead of ebp
, but that doesn't matter because we'll overwrite that soon anyway.
What's the nicest way to get the current flag byte at a pointer to print, though? In these, probably prefixing a call puts()
with push eax
, push esp
. eax
already having the character to compare to for the flag, esp
then being a pointer to it for puts()
.
We can take advantage of eax
being an argument to only push it once, meaning at the end of it we'll want write in something that calls puts()
but also preserves registers that have values that will be used - ebx
, ecx
, and esp
. ebx
and esp
are preserved by the ABI, so we only have to save ecx
ourselves. I went with something like push ecx; push eax; push esp; call sym.puts; pop eax; pop eax; pop ecx
. This gives us a total of 11 bytes of code to insert, so let's see if that's small enough...
In xorscura_compare_prng()
we'll want to patch at 0x8049ce9
, 0x8049d0e
, 0x8049d2a
, and 0x8049d46
. For all of these, we'll want to retain the xor al, byte [...]
and move it up appropriately so the printed character is still decoded!
If we're just patching out the comparison, the cmp
at 0x8049cf4
, 0x8049d1a
, 0x8049d38
, and 0x8049d54
aren't necessary anymore, nor are the movzx
into ebp
or edx
for the user input string to check against. We can also remove the jne
for when the strings are not equal too. That puts us at 4 + 3 + 2 + 6 = 15 bytes to work with in the first region, and 5 + 3 + 2 + 2 = 12 bytes in the others. The other comparisons against ebx + 1
, ebx + 2
, ebx + 3
are to check bounds against input sizes - since inputs are multiples of four we don't need these, but not removing them means less work. So let's not. The patch itself is 11 bytes, so this fits nicely as long as we move some code around.
0x8049ce9
The compiler used 0f85
and 0f84
for jne
and je
by 0x8049ce9
because the jump targets are >128 bytes forward, so we have a little extra room to work with here, but it's not actually too important.
Here we'll want to rewrite right after the movzx eax, byte [ecx + ebx]
, writing in: xor al, byte [esp + 0x1c] push ecx push eax push esp call sym.puts ; more on this in a moment pop eax ; to fix up the stack pop eax ; restore eax pop ecx ; restore ecx
This assembles to 3244241c515054e8XXXXXXXX585859
, for 15 bytes. At 0x8049ce9
we have 0fb6141f3244241c0fbec039c20f8584000000
, 19 bytes, so let's throw 66666690
at the end for a nop
to fill the extra space.
XXXXXXXX is an offset to the start of puts()
. Or, more specifically, where the loader fixed up a jmp
to go to puts()
I'm not sure where that would be, so the easiest thing I imagined was to look at what another puts()
goes. Say, the one in main
sounds good!
0x08048b94 e867fbffff call sym.imp.puts
The offset here is 0xfffffb67
, and the call destination is that offset plus the address of this instruction, plus the length of this instruction, which comes out to 0x8048b94 + 0xfffffb67 + 5
, for our puts()
target being at 0x8048700
. So in our patch, we'll want XXXXXXXX
to be 0x8048700 - (0x8049cf0 + 5)
, for 0xffffea0b
. Because little-endian, the whole instruction is written out as e80beaffff
.
Now we have a patch! 3244241c515054e80beaffff58585966666690 @ 0x8049ce9
.
0x8049d0e
Same process holds here, too. Same patch fits, but the nop
at the end will have to be differently sized to fit in the space - just 90
instead of the four byte version, here and for the next two.
We also need to be careful to xor against esp + 0x1d
here, not esp + 0x1c
again.
Repeating the process for computing a call destination, we get our patch: 3244241d515054e8e6e9ffff58585990 @ 0x8049d0e
0x8049d2a
Again, next byte from random_r()
, so xor al, byte [esp + 0x1e]
Same size region, so we just have to figure out the call destination here: 3244241e515054e8cae9ffff58585990 @ 0x8049d2a
0x8049d46
Yadda yadda yadda, xor against next byte, spoiler alert: 3244241f515054e8aee9ffff58585990 @ 0x8049d46
Aaaand patch!
Before:,==< 0x08049cd9 0f84d1000000 je 0x8049db0 || 0x08049cdf 8b4e0c mov ecx, dword [esi + 0xc] ; [0xc:4]=0 || 0x08049ce2 8b7e04 mov edi, dword [esi + 4] ; [0x4:4]=0x10101 || 0x08049ce5 0fb60419 movzx eax, byte [ecx + ebx] || 0x08049ce9 0fb6141f movzx edx, byte [edi + ebx] || 0x08049ced 3244241c xor al, byte [esp + 0x1c] || 0x08049cf1 0fbec0 movsx eax, al || 0x08049cf4 39c2 cmp edx, eax ,===< 0x08049cf6 0f8584000000 jne 0x8049d80 ||| 0x08049cfc 8b16 mov edx, dword [esi] ||| 0x08049cfe 8d4301 lea eax, [ebx + 1] ||| 0x08049d01 39c2 cmp edx, eax ,====< 0x08049d03 0f8497000000 je 0x8049da0 |||| 0x08049d09 0fb6441901 movzx eax, byte [ecx + ebx + 1] ; [0x1:1]=69 |||| 0x08049d0e 0fb66c1f01 movzx ebp, byte [edi + ebx + 1] ; [0x1:1]=69 |||| 0x08049d13 3244241d xor al, byte [esp + 0x1d] |||| 0x08049d17 0fbec0 movsx eax, al |||| 0x08049d1a 39c5 cmp ebp, eax ,=====< 0x08049d1c 7562 jne 0x8049d80 ||||| 0x08049d1e 8d4302 lea eax, [ebx + 2] ||||| 0x08049d21 39c2 cmp edx, eax ,======< 0x08049d23 747b je 0x8049da0 |||||| 0x08049d25 0fb6441902 movzx eax, byte [ecx + ebx + 2] ; [0x2:1]=76 |||||| 0x08049d2a 0fb66c1f02 movzx ebp, byte [edi + ebx + 2] ; [0x2:1]=76 |||||| 0x08049d2f 3244241e xor al, byte [esp + 0x1e] |||||| 0x08049d33 0fbec0 movsx eax, al |||||| 0x08049d36 39c5 cmp ebp, eax ,=======< 0x08049d38 7546 jne 0x8049d80 ||||||| 0x08049d3a 8d4303 lea eax, [ebx + 3] ; "F\x01\x01\x01" ||||||| 0x08049d3d 39c2 cmp edx, eax ========< 0x08049d3f 745f je 0x8049da0 ||||||| 0x08049d41 0fb6441903 movzx eax, byte [ecx + ebx + 3] ; [0x3:1]=70 ; "F\x01\x01\x01" ||||||| 0x08049d46 0fb67c1f03 movzx edi, byte [edi + ebx + 3] ; [0x3:1]=70 ; "F\x01\x01\x01" ||||||| 0x08049d4b 3244241f xor al, byte [esp + 0x1f] ||||||| 0x08049d4f 0fbec0 movsx eax, al ||||||| 0x08049d52 39c7 cmp edi, eax ========< 0x08049d54 752a jne 0x8049d80 ||||||| 0x08049d56 83c304 add ebx, 4 ||||||| 0x08049d59 39d3 cmp ebx, edx ||||||`=< 0x08049d5b 0f8261ffffff jb 0x8049cc2 |||||| 0x08049d61 83ec0c sub esp, 0xc |||||| 0x08049d64 ff742418 push dword [esp + 0x18] |||||| 0x08049d68 e813e9ffff call sym.imp.free |||||| 0x08049d6d 83c410 add esp, 0x10 |||||| 0x08049d70 31c0 xor eax, eax |||||| 0x08049d72 81c42c010000 add esp, 0x12c |||||| 0x08049d78 5b pop ebx |||||| 0x08049d79 5e pop esi |||||| 0x08049d7a 5f pop edi |||||| 0x08049d7b 5d pop ebp |||||| 0x08049d7c c3 ret -- Find wide-char strings with the '/w <string>' command [0x08048820]>
,=< 0x08049cd9 0f84d1000000 je 0x8049db0 | 0x08049cdf 8b4e0c mov ecx, dword [esi + 0xc] ; [0xc:4]=0 | 0x08049ce2 8b7e04 mov edi, dword [esi + 4] ; [0x4:4]=0x10101 | 0x08049ce5 0fb60419 movzx eax, byte [ecx + ebx] | 0x08049ce9 3244241c xor al, byte [esp + 0x1c] | 0x08049ced 51 push ecx | 0x08049cee 50 push eax | 0x08049cef 54 push esp | 0x08049cf0 e80beaffff call sym.imp.puts | 0x08049cf5 58 pop eax | 0x08049cf6 58 pop eax | 0x08049cf7 59 pop ecx | 0x08049cf8 66666690 nop | 0x08049cfc 8b16 mov edx, dword [esi] | 0x08049cfe 8d4301 lea eax, [ebx + 1] | 0x08049d01 39c2 cmp edx, eax ,==< 0x08049d03 0f8497000000 je 0x8049da0 || 0x08049d09 0fb6441901 movzx eax, byte [ecx + ebx + 1] ; [0x1:1]=69 || 0x08049d0e 3244241d xor al, byte [esp + 0x1d] || 0x08049d12 51 push ecx || 0x08049d13 50 push eax || 0x08049d14 54 push esp || 0x08049d15 e8e6e9ffff call sym.imp.puts || 0x08049d1a 58 pop eax || 0x08049d1b 58 pop eax || 0x08049d1c 59 pop ecx || 0x08049d1d 90 nop || 0x08049d1e 8d4302 lea eax, [ebx + 2] || 0x08049d21 39c2 cmp edx, eax ,===< 0x08049d23 747b je 0x8049da0 ||| 0x08049d25 0fb6441902 movzx eax, byte [ecx + ebx + 2] ; [0x2:1]=76 ||| 0x08049d2a 3244241e xor al, byte [esp + 0x1e] ||| 0x08049d2e 51 push ecx ||| 0x08049d2f 50 push eax ||| 0x08049d30 54 push esp ||| 0x08049d31 e8cae9ffff call sym.imp.puts ||| 0x08049d36 58 pop eax ||| 0x08049d37 58 pop eax ||| 0x08049d38 59 pop ecx ||| 0x08049d39 90 nop ||| 0x08049d3a 8d4303 lea eax, [ebx + 3] ; "F\x01\x01\x01" ||| 0x08049d3d 39c2 cmp edx, eax ,====< 0x08049d3f 745f je 0x8049da0 |||| 0x08049d41 0fb6441903 movzx eax, byte [ecx + ebx + 3] ; [0x3:1]=70 ; "F\x01\x01\x01" |||| 0x08049d46 3244241f xor al, byte [esp + 0x1f] |||| 0x08049d4a 51 push ecx |||| 0x08049d4b 50 push eax |||| 0x08049d4c 54 push esp |||| 0x08049d4d e8aee9ffff call sym.imp.puts |||| 0x08049d52 58 pop eax |||| 0x08049d53 58 pop eax
Wonderful - they are in fact correctly lined up calls to puts()
, everything seems to line up... Now we should be able to run the program and, regardless of input, it'll print out flags.
Well, it does.
+400
Flag 5
/ (fcn) sym.check_flag_5 710 | sym.check_flag_5 (); | ; var int local_8h @ ebp-0x8 | ; var int local_40h @ ebp-0x40 | ; var int local_3ch @ ebp-0x3c | ; var int local_38h @ ebp-0x38 | ; var int local_28h @ ebp-0x28 | ; var int local_34h @ ebp-0x34 | ; var int local_30h @ ebp-0x30 | ; var int local_2ch @ ebp-0x2c | ; var int local_24h @ ebp-0x24 | ; var int local_98h @ ebp-0x98 | ; var int local_1ch @ ebp-0x1c | ; var int local_65h @ ebp-0x65 | ; var int local_66h @ ebp-0x66 | ; var int local_67h @ ebp-0x67 | ; var int local_68h @ ebp-0x68 | ; var int local_69h @ ebp-0x69 | ; var int local_6ah @ ebp-0x6a | ; var int local_6bh @ ebp-0x6b | ; var int local_6ch @ ebp-0x6c | ; var int local_6dh @ ebp-0x6d | ; var int local_6eh @ ebp-0x6e | ; var int local_6fh @ ebp-0x6f | ; var int local_70h @ ebp-0x70 | ; var int local_71h @ ebp-0x71 | ; var int local_72h @ ebp-0x72 | ; var int local_73h @ ebp-0x73 | ; var int local_74h @ ebp-0x74 | ; var int local_75h @ ebp-0x75 | ; var int local_76h @ ebp-0x76 | ; var int local_77h @ ebp-0x77 | ; var int local_78h @ ebp-0x78 | ; var int local_79h @ ebp-0x79 | ; var int local_7ah @ ebp-0x7a | ; var int local_7bh @ ebp-0x7b | ; var int local_7ch @ ebp-0x7c | ; var int local_7dh @ ebp-0x7d | ; var int local_7eh @ ebp-0x7e | ; var int local_7fh @ ebp-0x7f | ; var int local_80h @ ebp-0x80 | ; var int local_81h @ ebp-0x81 | ; var int local_82h @ ebp-0x82 | ; var int local_83h @ ebp-0x83 | ; var int local_84h @ ebp-0x84 | ; var int local_85h @ ebp-0x85 | ; var int local_86h @ ebp-0x86 | ; var int local_87h @ ebp-0x87 | ; var int local_88h @ ebp-0x88 | ; var int local_41h @ ebp-0x41 | ; var int local_42h @ ebp-0x42 | ; var int local_43h @ ebp-0x43 | ; var int local_44h @ ebp-0x44 | ; var int local_45h @ ebp-0x45 | ; var int local_46h @ ebp-0x46 | ; var int local_47h @ ebp-0x47 | ; var int local_48h @ ebp-0x48 | ; var int local_49h @ ebp-0x49 | ; var int local_4ah @ ebp-0x4a | ; var int local_4bh @ ebp-0x4b | ; var int local_4ch @ ebp-0x4c | ; var int local_4dh @ ebp-0x4d | ; var int local_4eh @ ebp-0x4e | ; var int local_4fh @ ebp-0x4f | ; var int local_50h @ ebp-0x50 | ; var int local_51h @ ebp-0x51 | ; var int local_52h @ ebp-0x52 | ; var int local_53h @ ebp-0x53 | ; var int local_54h @ ebp-0x54 | ; var int local_55h @ ebp-0x55 | ; var int local_56h @ ebp-0x56 | ; var int local_57h @ ebp-0x57 | ; var int local_58h @ ebp-0x58 | ; var int local_59h @ ebp-0x59 | ; var int local_5ah @ ebp-0x5a | ; var int local_5bh @ ebp-0x5b | ; var int local_5ch @ ebp-0x5c | ; var int local_5dh @ ebp-0x5d | ; var int local_5eh @ ebp-0x5e | ; var int local_5fh @ ebp-0x5f | ; var int local_60h @ ebp-0x60 | ; var int local_61h @ ebp-0x61 | ; var int local_62h @ ebp-0x62 | ; var int local_63h @ ebp-0x63 | ; var int local_64h @ ebp-0x64 | ; var int local_4h @ esp+0x4 | ; CALL XREF from 0x08048b2f (sym.main) | 0x0804959c 8d4c2404 lea ecx, [local_4h] | 0x080495a0 83e4e0 and esp, 0xffffffe0 | 0x080495a3 ff71fc push dword [ecx - 4] | 0x080495a6 55 push ebp | 0x080495a7 89e5 mov ebp, esp | 0x080495a9 53 push ebx | 0x080495aa 51 push ecx | 0x080495ab 81ec90000000 sub esp, 0x90 | 0x080495b1 89cb mov ebx, ecx | 0x080495b3 c6459ccd mov byte [local_64h], 0xcd | 0x080495b7 c6459d2a mov byte [local_63h], 0x2a ; '*' | 0x080495bb c6459e8b mov byte [local_62h], 0x8b | 0x080495bf c6459f3f mov byte [local_61h], 0x3f ; '?' | 0x080495c3 c645a09d mov byte [local_60h], 0x9d | 0x080495c7 c645a18a mov byte [local_5fh], 0x8a | 0x080495cb c645a28d mov byte [local_5eh], 0x8d | 0x080495cf c645a33e mov byte [local_5dh], 0x3e ; '>' | 0x080495d3 c645a464 mov byte [local_5ch], 0x64 ; 'd' | 0x080495d7 c645a55a mov byte [local_5bh], 0x5a ; 'Z' | 0x080495db c645a683 mov byte [local_5ah], 0x83 | 0x080495df c645a74f mov byte [local_59h], 0x4f ; 'O' | 0x080495e3 c645a87c mov byte [local_58h], 0x7c ; '|' | 0x080495e7 c645a950 mov byte [local_57h], 0x50 ; 'P' | 0x080495eb c645aab9 mov byte [local_56h], 0xb9 | 0x080495ef c645ab32 mov byte [local_55h], 0x32 ; '2' | 0x080495f3 c645acd2 mov byte [local_54h], 0xd2 | 0x080495f7 c645adfe mov byte [local_53h], 0xfe | 0x080495fb c645ae3b mov byte [local_52h], 0x3b ; ';' | 0x080495ff c645af1a mov byte [local_51h], 0x1a | 0x08049603 c645b03c mov byte [local_50h], 0x3c ; '<' | 0x08049607 c645b198 mov byte [local_4fh], 0x98 | 0x0804960b c645b26d mov byte [local_4eh], 0x6d ; 'm' | 0x0804960f c645b33b mov byte [local_4dh], 0x3b ; ';' | 0x08049613 c645b4b4 mov byte [local_4ch], 0xb4 | 0x08049617 c645b5eb mov byte [local_4bh], 0xeb | 0x0804961b c645b660 mov byte [local_4ah], 0x60 ; '`' | 0x0804961f c645b772 mov byte [local_49h], 0x72 ; 'r' | 0x08049623 c645b8f8 mov byte [local_48h], 0xf8 | 0x08049627 c645b9f5 mov byte [local_47h], 0xf5 | 0x0804962b c645ba46 mov byte [local_46h], 0x46 ; 'F' | 0x0804962f c645bb35 mov byte [local_45h], 0x35 ; '5' | 0x08049633 c645bca6 mov byte [local_44h], 0xa6 | 0x08049637 c645bdce mov byte [local_43h], 0xce | 0x0804963b c645be70 mov byte [local_42h], 0x70 ; 'p' | 0x0804963f c645bf6c mov byte [local_41h], 0x6c ; 'l' | 0x08049643 c68578ffffff. mov byte [local_88h], 0x7d ; '}' | 0x0804964a c68579ffffff. mov byte [local_87h], 0xf0 | 0x08049651 c6857affffff. mov byte [local_86h], 0xc5 | 0x08049658 c6857bffffff. mov byte [local_85h], 0x6a ; 'j' | 0x0804965f c6857cffffff. mov byte [local_84h], 0x5c ; '\' | 0x08049666 c6857dffffff. mov byte [local_83h], 0xaa | 0x0804966d c6857effffff. mov byte [local_82h], 0x15 | 0x08049674 c6857fffffff. mov byte [local_81h], 0x63 ; 'c' | 0x0804967b c645805e mov byte [local_80h], 0x5e ; '^' | 0x0804967f c6458155 mov byte [local_7fh], 0x55 ; 'U' | 0x08049683 c6458237 mov byte [local_7eh], 0x37 ; '7' | 0x08049687 c645831f mov byte [local_7dh], 0x1f | 0x0804968b c6458406 mov byte [local_7ch], 6 | 0x0804968f c64585e2 mov byte [local_7bh], 0xe2 | 0x08049693 c64586b8 mov byte [local_7ah], 0xb8 | 0x08049697 c6458758 mov byte [local_79h], 0x58 ; 'X' | 0x0804969b c64588f1 mov byte [local_78h], 0xf1 | 0x0804969f c64589fa mov byte [local_77h], 0xfa | 0x080496a3 c6458aac mov byte [local_76h], 0xac | 0x080496a7 c6458b4d mov byte [local_75h], 0x4d ; 'M' | 0x080496ab c6458c24 mov byte [local_74h], 0x24 ; '$' | 0x080496af c6458dd7 mov byte [local_73h], 0xd7 | 0x080496b3 c6458ed3 mov byte [local_72h], 0xd3 | 0x080496b7 c6458f22 mov byte [local_71h], 0x22 ; '"' | 0x080496bb c6459064 mov byte [local_70h], 0x64 ; 'd' | 0x080496bf c64591c1 mov byte [local_6fh], 0xc1 | 0x080496c3 c6459283 mov byte [local_6eh], 0x83 | 0x080496c7 c6459353 mov byte [local_6dh], 0x53 ; 'S' | 0x080496cb c64594a3 mov byte [local_6ch], 0xa3 | 0x080496cf c6459531 mov byte [local_6bh], 0x31 ; '1' | 0x080496d3 c64596bf mov byte [local_6ah], 0xbf | 0x080496d7 c645974b mov byte [local_69h], 0x4b ; 'K' | 0x080496db c64598f0 mov byte [local_68h], 0xf0 | 0x080496df c64599c1 mov byte [local_67h], 0xc1 | 0x080496e3 c6459a05 mov byte [local_66h], 5 | 0x080496e7 c6459b03 mov byte [local_65h], 3 | 0x080496eb c745e4000000. mov dword [local_1ch], 0 | 0x080496f2 8d459c lea eax, [local_64h] | 0x080496f5 898568ffffff mov dword [local_98h], eax | 0x080496fb 666666666666. nop | 0x08049707 666666666666. nop | 0x08049714 90 nop | 0x08049715 90 nop | 0x08049716 90 nop | 0x08049717 90 nop | 0x08049718 90 nop | 0x08049719 90 nop | 0x0804971a 90 nop | 0x0804971b 90 nop | 0x0804971c 90 nop | 0x0804971d 90 nop | 0x0804971e 90 nop | 0x0804971f 90 nop | 0x08049720 90 nop | 0x08049721 90 nop | 0x08049722 90 nop | 0x08049723 90 nop | 0x08049724 90 nop | 0x08049725 90 nop | 0x08049726 90 nop | 0x08049727 90 nop | 0x08049728 90 nop | 0x08049729 90 nop | 0x0804972a 90 nop | 0x0804972b 90 nop | 0x0804972c 90 nop | 0x0804972d 90 nop | 0x0804972e 90 nop | 0x0804972f 90 nop | 0x08049730 90 nop | 0x08049731 90 nop | 0x08049732 90 nop | 0x08049733 90 nop | 0x08049734 90 nop | 0x08049735 90 nop | 0x08049736 6865980408 push 0x8049865 | 0x0804973b 8d8568ffffff lea eax, [local_98h] | 0x08049741 50 push eax | 0x08049742 58 pop eax | 0x08049743 5b pop ebx | 0x08049744 8918 mov dword [eax], ebx | 0x08049746 90 nop | 0x08049747 90 nop | 0x08049748 90 nop | 0x08049749 90 nop | 0x0804974a 90 nop | 0x0804974b 90 nop | 0x0804974c 90 nop | 0x0804974d 90 nop | 0x0804974e 90 nop | 0x0804974f 90 nop | 0x08049750 90 nop | 0x08049751 90 nop | 0x08049752 90 nop | 0x08049753 90 nop | 0x08049754 90 nop | 0x08049755 90 nop | 0x08049756 90 nop | 0x08049757 90 nop | 0x08049758 90 nop | 0x08049759 90 nop | 0x0804975a 90 nop | 0x0804975b 90 nop | 0x0804975c 90 nop | 0x0804975d 90 nop | 0x0804975e 90 nop | 0x0804975f 90 nop | 0x08049760 90 nop | 0x08049761 90 nop | 0x08049762 90 nop | 0x08049763 90 nop | 0x08049764 90 nop | 0x08049765 90 nop | 0x08049766 90 nop | 0x08049767 90 nop | 0x08049768 90 nop | 0x08049769 90 nop | 0x0804976a 90 nop | 0x0804976b 90 nop | 0x0804976c 90 nop | 0x0804976d 90 nop | 0x0804976e 90 nop | 0x0804976f 90 nop | 0x08049770 90 nop | 0x08049771 90 nop | 0x08049772 90 nop | 0x08049773 90 nop | 0x08049774 90 nop | 0x08049775 90 nop | 0x08049776 90 nop | 0x08049777 90 nop | 0x08049778 90 nop | 0x08049779 90 nop | 0x0804977a 90 nop | 0x0804977b 90 nop | 0x0804977c 83ec08 sub esp, 8 | 0x0804977f 6a18 push 0x18 ; " \x88\x04\b4" | 0x08049781 6a01 push 1 | 0x08049783 e888f0ffff call sym.imp.calloc ; void *calloc(size_t nmeb, size_t size) | 0x08049788 83c410 add esp, 0x10 | 0x0804978b 8945dc mov dword [local_24h], eax | 0x0804978e 837ddc00 cmp dword [local_24h], 0 | ,=< 0x08049792 752b jne 0x80497bf | | 0x08049794 e8e7efffff call sym.imp.__errno_location | | 0x08049799 8b00 mov eax, dword [eax] | | 0x0804979b c745d4ffffff. mov dword [local_2ch], 0xffffffff | | 0x080497a2 8945d0 mov dword [local_30h], eax | | 0x080497a5 c745ccfda604. mov dword [local_34h], str.calloc_1___d_ | | 0x080497ac 6a18 push 0x18 ; " \x88\x04\b4" | | 0x080497ae ff75cc push dword [local_34h] | | 0x080497b1 ff75d0 push dword [local_30h] | | 0x080497b4 ff75d4 push dword [local_2ch] | | 0x080497b7 e834efffff call sym.imp.error | | 0x080497bc 83c410 add esp, 0x10 | | ; JMP XREF from 0x08049792 (sym.check_flag_5) | `-> 0x080497bf 8b45dc mov eax, dword [local_24h] | 0x080497c2 c70024000000 mov dword [eax], 0x24 ; '$' ; [0x24:4]=0 ; '$' | 0x080497c8 8b9568ffffff mov edx, dword [local_98h] | 0x080497ce 8b45dc mov eax, dword [local_24h] | 0x080497d1 895008 mov dword [eax + 8], edx | 0x080497d4 8b45dc mov eax, dword [local_24h] | 0x080497d7 8d9578ffffff lea edx, [local_88h] | 0x080497dd 89500c mov dword [eax + 0xc], edx | 0x080497e0 8b45dc mov eax, dword [local_24h] | 0x080497e3 8b13 mov edx, dword [ebx] | 0x080497e5 895004 mov dword [eax + 4], edx | 0x080497e8 83ec0c sub esp, 0xc | 0x080497eb ff75dc push dword [local_24h] | 0x080497ee e8cd050000 call sym.xorscura_compare | 0x080497f3 83c410 add esp, 0x10 | 0x080497f6 8945d8 mov dword [local_28h], eax | 0x080497f9 837dd8ff cmp dword [local_28h], 0xffffffffffffffff | ,=< 0x080497fd 752d jne 0x804982c | | 0x080497ff 8b5ddc mov ebx, dword [local_24h] | | 0x08049802 e879efffff call sym.imp.__errno_location | | 0x08049807 8b00 mov eax, dword [eax] | | 0x08049809 c745c8ffffff. mov dword [local_38h], 0xffffffff | | 0x08049810 8945c4 mov dword [local_3ch], eax | | 0x08049813 c745c00ba704. mov dword [local_40h], str.xorscura_compare__lx_ | | 0x0804981a 53 push ebx | | 0x0804981b ff75c0 push dword [local_40h] | | 0x0804981e ff75c4 push dword [local_3ch] | | 0x08049821 ff75c8 push dword [local_38h] | | 0x08049824 e8c7eeffff call sym.imp.error | | 0x08049829 83c410 add esp, 0x10 | | ; JMP XREF from 0x080497fd (sym.check_flag_5) | `-> 0x0804982c 837dd800 cmp dword [local_28h], 0 | ,=< 0x08049830 7507 jne 0x8049839 | | 0x08049832 c745e4010000. mov dword [local_1ch], 1 | | ; JMP XREF from 0x08049830 (sym.check_flag_5) | `-> 0x08049839 83ec0c sub esp, 0xc | 0x0804983c ff75dc push dword [local_24h] | 0x0804983f e80c060000 call sym.xorscura_free_xod ; void free(void *ptr) | 0x08049844 83c410 add esp, 0x10 | 0x08049847 83ec0c sub esp, 0xc | 0x0804984a ff75dc push dword [local_24h] | 0x0804984d e82eeeffff call sym.imp.free ; void free(void *ptr) | 0x08049852 83c410 add esp, 0x10 | 0x08049855 8b45e4 mov eax, dword [local_1ch] | 0x08049858 8d65f8 lea esp, [local_8h] | 0x0804985b 59 pop ecx | 0x0804985c 5b pop ebx | 0x0804985d 5d pop ebp | 0x0804985e 8d61fc lea esp, [ecx - 4] \ 0x08049861 c3 ret
Interestingly, after the prior change, not much was necessary to make flag-5
work out. Just run the program and copy out the flag. Almost.
Except that this check, it turns out, uses the comparison in xorscura_compare
rather than xorscura_compare_prng
, so we'll have to go back and patch that the same way.
/ (fcn) sym.xorscura_compare 117 | sym.xorscura_compare (int arg_14h); | ! ; arg int arg_14h @ esp+0x14 | ! ; CALL XREF from 0x08049528 (sym.check_flag_4) | ! ; CALL XREF from 0x080497ee (sym.check_flag_5) | ! ; CALL XREF from 0x0804916f (sym.check_flag_3) | | 0x08049dc0 55 push ebp | | 0x08049dc1 57 push edi | | 0x08049dc2 56 push esi | | 0x08049dc3 53 push ebx | | 0x08049dc4 8b442414 mov eax, dword [arg_14h] ; [0x14:4]=1 | | 0x08049dc8 8b5010 mov edx, dword [eax + 0x10] ; [0x10:4]=0x30002 | | 0x08049dcb 85d2 test edx, edx | ,==< 0x08049dcd 7411 je 0x8049de0 | || 0x08049dcf 89442414 mov dword [arg_14h], eax | || 0x08049dd3 5b pop ebx | || 0x08049dd4 5e pop esi | || 0x08049dd5 5f pop edi | || 0x08049dd6 5d pop ebp | |`=< 0x08049dd7 e984feffff jmp sym.xorscura_compare_prng | 0x08049ddc 8d742600 lea esi, [esi] | | ; JMP XREF from 0x08049dcd (sym.xorscura_compare) | `--> 0x08049de0 8b5808 mov ebx, dword [eax + 8] ; [0x8:4]=0 | 0x08049de3 85db test ebx, ebx | ,=< 0x08049de5 7453 je 0x8049e3a | | 0x08049de7 8b28 mov ebp, dword [eax] | | 0x08049de9 85ed test ebp, ebp | ,==< 0x08049deb 7e3c jle 0x8049e29 | || 0x08049ded 8b700c mov esi, dword [eax + 0xc] ; [0xc:4]=0 | || 0x08049df0 8b7804 mov edi, dword [eax + 4] ; [0x4:4]=0x10101 | || 0x08049df3 0fb603 movzx eax, byte [ebx] | || 0x08049df6 3206 xor al, byte [esi] | || 0x08049df8 0fb617 movzx edx, byte [edi] | || 0x08049dfb 0fbec0 movsx eax, al | || 0x08049dfe 39c2 cmp edx, eax | ,===< 0x08049e00 752e jne 0x8049e30 | ||| 0x08049e02 31c0 xor eax, eax | ,====< 0x08049e04 eb1c jmp 0x8049e22 |||| 0x08049e06 8d7600 lea esi, [esi] |||| 0x08049e09 8dbc27000000. lea edi, [edi] | |||| ; JMP XREF from 0x08049e27 (sym.xorscura_compare) | .-----> 0x08049e10 0fb61403 movzx edx, byte [ebx + eax] | ||||| 0x08049e14 321406 xor dl, byte [esi + eax] | ||||| 0x08049e17 52 push edx | ||||| 0x08049e18 54 push esp | ||||| 0x08049e19 e8e2e8ffff call sym.imp.puts ; int puts(const char *s) | ||||| 0x08049e1e 5a pop edx | ||||| 0x08049e1f 5a pop edx | ||||| 0x08049e20 66666690 nop | | ||| 0x08049e24 40 inc eax | | ||| 0x08049e25 39e8 cmp eax, ebp | `=====< 0x08049e27 75e7 jne 0x8049e10 | ||| ; JMP XREF from 0x08049deb (sym.xorscura_compare) | |`--> 0x08049e29 31c0 xor eax, eax | | | ; JMP XREF from 0x08049e3f (sym.xorscura_compare) | |.--> 0x08049e2b 5b pop ebx | ||| 0x08049e2c 5e pop esi | ||| 0x08049e2d 5f pop edi | ||| 0x08049e2e 5d pop ebp | ||| 0x08049e2f c3 ret | |!| ; JMP XREF from 0x08049e00 (sym.xorscura_compare) | `---> 0x08049e30 5b pop ebx | || 0x08049e31 b801000000 mov eax, 1 | || 0x08049e36 5e pop esi | || 0x08049e37 5f pop edi | || 0x08049e38 5d pop ebp | || 0x08049e39 c3 ret | !| ; JMP XREF from 0x08049de5 (sym.xorscura_compare) | |`-> 0x08049e3a b8ffffffff mov eax, 0xffffffff ; -1 \ `==< 0x08049e3f ebea jmp 0x8049e2b
There's a xor
in a loop and a xor
not in a loop, which is curious. It turns out the first byte of the user input vs decoded flag is tested before even entering the loop! We can patch out the jmp
at 0x8049e00
and let execution fall through into the loop with eax
starting at 0, which will result in the first flag byte being computed twice. This lets us only patch the loop body, which is a bit easier.
6690 @ 0x8049e00
does the trick here:
/ (fcn) sym.xorscura_compare 454 | sym.xorscura_compare (int arg_148h); | ! ; var int local_ch @ esp+0xc | ! ; var int local_14h @ esp+0x14 | ! ; var int local_18h @ esp+0x18 | ! ; var int local_1ch @ esp+0x1c | ! ; var int local_1dh @ esp+0x1d | ! ; var int local_1eh @ esp+0x1e | ! ; var int local_1fh @ esp+0x1f | ! ; var int local_20h @ esp+0x20 | ! ; var int local_24h @ esp+0x24 | ! ; arg int arg_148h @ esp+0x148 | ! ; CALL XREF from 0x0804916f (sym.check_flag_3) | ! ; CALL XREF from 0x08049528 (sym.check_flag_4) | ! ; CALL XREF from 0x080497ee (sym.check_flag_5) | | 0x08049dc0 55 push ebp | | 0x08049dc1 57 push edi | | 0x08049dc2 56 push esi | | 0x08049dc3 53 push ebx | | 0x08049dc4 8b442414 mov eax, dword [local_14h] ; [0x14:4]=1 | | 0x08049dc8 8b5010 mov edx, dword [eax + 0x10] ; [0x10:4]=0x30002 | | 0x08049dcb 85d2 test edx, edx | ,==< 0x08049dcd 7411 je 0x8049de0 | || 0x08049dcf 89442414 mov dword [local_14h], eax | || 0x08049dd3 5b pop ebx | || 0x08049dd4 5e pop esi | || 0x08049dd5 5f pop edi | || 0x08049dd6 5d pop ebp | |`=< 0x08049dd7 e984feffff jmp sym.xorscura_compare_prng | 0x08049ddc 8d742600 lea esi, [esi] | | ; JMP XREF from 0x08049dcd (sym.xorscura_compare) | `--> 0x08049de0 8b5808 mov ebx, dword [eax + 8] ; [0x8:4]=0 | 0x08049de3 85db test ebx, ebx | ,=< 0x08049de5 7453 je 0x8049e3a | | 0x08049de7 8b08 mov ecx, dword [eax] | | 0x08049de9 85c9 test ecx, ecx | ,==< 0x08049deb 7e3c jle 0x8049e29 | || 0x08049ded 8b700c mov esi, dword [eax + 0xc] ; [0xc:4]=0 | || 0x08049df0 8b7804 mov edi, dword [eax + 4] ; [0x4:4]=0x10101 | || 0x08049df3 0fb603 movzx eax, byte [ebx] | || 0x08049df6 3206 xor al, byte [esi] | || 0x08049df8 0fb617 movzx edx, byte [edi] | || 0x08049dfb 0fbec0 movsx eax, al | || 0x08049dfe 39c2 cmp edx, eax | ,===< 0x08049e00 752e jne 0x8049e30 | ||| 0x08049e02 31c0 xor eax, eax | ,====< 0x08049e04 eb1c jmp 0x8049e22 |||| 0x08049e06 8d7600 lea esi, [esi] |||| 0x08049e09 8dbc27000000. lea edi, [edi] | |||| ; JMP XREF from 0x08049e27 (sym.xorscura_compare) | .-----> 0x08049e10 0fb61403 movzx edx, byte [ebx + eax] | ||||| 0x08049e14 0fb62c07 movzx ebp, byte [edi + eax] | ||||| 0x08049e18 321406 xor dl, byte [esi + eax] | ||||| 0x08049e1b 0fbed2 movsx edx, dl | ||||| 0x08049e1e 39d5 cmp ebp, edx | ,======< 0x08049e20 750e jne 0x8049e30 | |!|||| ; JMP XREF from 0x08049e04 (sym.xorscura_compare) | ||`----> 0x08049e22 83c001 add eax, 1 | || ||| 0x08049e25 39c8 cmp eax, ecx | |`=====< 0x08049e27 75e7 jne 0x8049e10 | | ||| ; JMP XREF from 0x08049deb (sym.xorscura_compare) | | |`--> 0x08049e29 31c0 xor eax, eax | | | | ; JMP XREF from 0x08049e3f (sym.xorscura_compare) | | |.--> 0x08049e2b 5b pop ebx | | ||| 0x08049e2c 5e pop esi | | ||| 0x08049e2d 5f pop edi | | ||| 0x08049e2e 5d pop ebp | | ||| 0x08049e2f c3 ret | | |!| ; JMP XREF from 0x08049e00 (sym.xorscura_compare) | | |!| ; JMP XREF from 0x08049e20 (sym.xorscura_compare) | `--`---> 0x08049e30 5b pop ebx | || 0x08049e31 b801000000 mov eax, 1 | || 0x08049e36 5e pop esi | || 0x08049e37 5f pop edi | || 0x08049e38 5d pop ebp | || 0x08049e39 c3 ret | !| ; JMP XREF from 0x08049de5 (sym.xorscura_compare) | |`-> 0x08049e3a b8ffffffff mov eax, 0xffffffff ; -1 \ `==< 0x08049e3f ebea jmp 0x8049e2b
Looking back to the loop body, we have edx
being xor
'd, so that's probably where flag bytes pass through. We can get rid of movzx ebp, byte [edi + eax]
, movsx edx, dl
, cmp ebp, edx
, and jne 0x8049e30
to make space for our patch, giving 4 + 3 + 3 + 2 + 2 = 14 bytes.
ecx
holds the lenth of the flag, so we still have to save that, but eax
is a counter here, and the flag byte is sitting in edx
. To make matters worse, ebx
is also used to hold the pointer into the xor'd flag string.
Since we won't be needing the user-input string, which was in edi
, we can use edi
for the flag string instead, letting the ABI keep it preserved for us.
Patching 58
at 0x8049de1
to 78
changes the destination of mov ebx, dword [eax + 8]
to edi
, nop
'ing mov edi, dword [eax + 4]
at 0x08049df0
keeps edi
safe, and patching 03
at 0x8049e13
to 07
changes movzx edx, byte [ebx + eax]
to movzx edx, byte [edi + eax]
. Now we don't have to worry about preserving ebx
through puts()
calls.
This means 16 bytes of patch code, in a 14 byte hole. Thankfully, we can get two more bytes by replacing add eax, 1
at 0x8049e22
with inc eax
, which is just 40`. Now it's 16 bytes of code into a 16-byte hole, fitting perfectly:
3214065151505254e8e1e8ffff5a5a5859 @ 0x8049e14
and now we've patched it up to print yet another flag when run!
For this one, run without sudo, the last flag prints as gibberish because ptrace()
to poke around in memory silently fails, pointing at incorrect data for the flag. sudo ./Sheb-Teth.bin
, however, will print out the last flag.
+500 and we're done!
At the end you'll have a binary that looks very much like mine for this writeup
I absolutely glazed over the anti-debugging for flags 4 and 5, but that's entirely because I have no idea how to get gdb to trace properly through the forking and ptrace'ing - setting it to follow child processes simply didn't! Reading through that trickery and figuring out how it worked was quite entertaining, highly recommended as an exercise left to the reader.
PS: the binary I had in finishing the challenges was a little different. My initial attempt included me wanting to debug into a segfault I caused by patching xorscura_compare
wrong (I trashed a register that shouldn't have been.. oops), so I actually looked into the ptraces and mimicked the IPC via POKEDATA
with a mov
. I also intially nop
'd out all the ptrace and fork nonsense after that, since it was throwing GDB off the scent. That's not necessary if we just get the patching right from the first time around, perhaps after knowing what not to do..
BONUS:
I fixed up some issues in the binary after my patching, now this binary will print all of the flags when run, no longer prompting for input.
The only extra patching necessary is really to replace strncmp()
calls with puts()
and ensure the right parameter is passed for flags 0, 1, and 2. In xorscura_compare()
, patching out comparisons as described above does the trick just fine.
Mind you, this writeup is much cleaner than the actual hacking I did at this binary during the CTF - I elided a lot of false starts and broken assembly and register fiddling.
1: When I say "patch", I do mean quite literally typing bytes into a hex editor. I personally use hexedit, but would happily accept suggestions for better terminal-based hex editors. Bless and HxD are also good, depending on your OS and preferences. Addresses like 0x804XXXX
here refer to addresses in the virtual address space of the process - to convert those to addresses in the file, for this particular binary, you would subtract the base address the file expects to be loaded at. For this program it's 0x804800
, which you can find through a little guesswork or looking at ELF headers with objdump -p Sheb-Teth.bin
:
Program Header: PHDR off 0x00000034 vaddr 0x08048034 paddr 0x08048034 align 2**2 filesz 0x00000100 memsz 0x00000100 flags r-x INTERP off 0x00000134 vaddr 0x08048134 paddr 0x08048134 align 2**0 filesz 0x00000013 memsz 0x00000013 flags r-- LOAD off 0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12 filesz 0x00002f68 memsz 0x00002f68 flags r-x LOAD off 0x00003000 vaddr 0x0804b000 paddr 0x0804b000 align 2**12 filesz 0x000001a8 memsz 0x000001b8 flags rw- DYNAMIC off 0x0000300c vaddr 0x0804b00c paddr 0x0804b00c align 2**2 filesz 0x000000e8 memsz 0x000000e8 flags rw- NOTE off 0x00000148 vaddr 0x08048148 paddr 0x08048148 align 2**2 filesz 0x00000044 memsz 0x00000044 flags r-- EH_FRAME off 0x000028d4 vaddr 0x0804a8d4 paddr 0x0804a8d4 align 2**2 filesz 0x000000a4 memsz 0x000000a4 flags r-- STACK off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**4 filesz 0x00000000 memsz 0x00000000 flags rw- LOAD off 0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12
The bolded entry here tells us that the region at file offset 0 maps to the virtual address 0x8048000, with the file and in-memory regions being the following 0x2f68 bytes. If you see a virtual address below 0x8048000 + 0x2f68
, this is the mapping that applies!
In radare2, you could instead ?p <virtual_addr>
to convert to a file offset, and not worry about these specifics.