The Codegate CTF 2019 Preliminary (ctftime.org) ran from 26/01/2019, 00:00 UTC to 27/01/2019 00:00 UTC.
Within this article I want to share my quick writeup on the challenge KingMaker.
KingMaker
The challenge description merely contains the ip/port of the CTF server running the challenge (nc 110.10.147.104 13152
) as well as a link to the corresponding binary:
root@kali:~/Documents/cg19/kingmaker# file KingMaker
KingMaker: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=bc9a6f99c771d8f8d72b46d48cdbbd5cde5d62a8, stripped
The program is a text adventure, which allows you to choose different actions:
root@kali:~/Documents/cg19/kingmaker# ./KingMaker
_ ___ __ __ _
| |/ (_)_ __ __ _| \/ | __ _| | _____ _ __
| ' /| | '_ \ / _` | |\/| |/ _` | |/ / _ \ '__|
| . \| | | | | (_| | | | | (_| | < __/ |
|_|\_\_|_| |_|\__, |_| |_|\__,_|_|\_\___|_|
|___/
Once upon a time, there was a kingdom with 7 princes.
One day the king thinks to decide the who will be the next king.
So he made 5 tests for the princes.
If you pass all the tests, you can be a king!
**********************KING MAKER START**********************
...
.....
.......
Who am I...??
1> Ask to someone
2> Look around
After the first choice we have to enter a key to proceed:
2
You : There's a book. Let's loot at it.
You : 7th prince...?
Servant : Yes you are the 7th prince of this country.
You : Am I....??
Servant : The king calls the prince!
...
.....
.......
King : I'm too old to rule the kingdom.
King : So I will give you few tests and choose the next king.
King : The only one prince who passes all the tests can be the king.
King : If you want to participate at this test, Enter the key for test 1
Inspecting the binary using radare2 we can see that the function printing the flag (system("/bin/cat ./flag")
) requires that the following highlighted values are equal to 5:
[0x00400920]> pdf @ sub.puts_b58
/ (fcn) sub.puts_b58 130
| sub.puts_b58 (char *arg1, unsigned int arg2);
| ; var unsigned int local_ch @ rbp-0xc
| ; var char *s @ rbp-0x8
| ; arg char *arg1 @ rdi
| ; arg unsigned int arg2 @ rsi
| ; XREFS(23)
| 0x00400b58 55 push rbp
| 0x00400b59 4889e5 mov rbp, rsp
| 0x00400b5c 4883ec10 sub rsp, 0x10
| 0x00400b60 48897df8 mov qword [s], rdi ; arg1
| 0x00400b64 8975f4 mov dword [local_ch], esi ; arg2
| 0x00400b67 488b45f8 mov rax, qword [s]
| 0x00400b6b 4889c7 mov rdi, rax ; const char *s
| 0x00400b6e e8bdfcffff call sym.imp.puts ; int puts(const char *s)
| 0x00400b73 837df401 cmp dword [local_ch], 1 ; [0x1:4]=-1 ; 1
| ,=< 0x00400b77 7557 jne 0x400bd0
| | 0x00400b79 8b05ed652000 mov eax, dword [0x0060716c] ; [0x60716c:4]=0
| | 0x00400b7f 83f805 cmp eax, 5 ; 5
| ,==< 0x00400b82 7542 jne 0x400bc6
| || 0x00400b84 8b05e6652000 mov eax, dword [0x00607170] ; [0x607170:4]=0
| || 0x00400b8a 83f805 cmp eax, 5 ; 5
| ,===< 0x00400b8d 7537 jne 0x400bc6
| ||| 0x00400b8f 8b05df652000 mov eax, dword [0x00607174] ; [0x607174:4]=0
| ||| 0x00400b95 83f805 cmp eax, 5 ; 5
| ,====< 0x00400b98 752c jne 0x400bc6
| |||| 0x00400b9a 8b05d8652000 mov eax, dword [0x00607178] ; [0x607178:4]=0
| |||| 0x00400ba0 83f805 cmp eax, 5 ; 5
| ,=====< 0x00400ba3 7521 jne 0x400bc6
| ||||| 0x00400ba5 8b05d1652000 mov eax, dword [0x0060717c] ; [0x60717c:4]=0
| ||||| 0x00400bab 83f805 cmp eax, 5 ; 5
| ,======< 0x00400bae 7516 jne 0x400bc6
| |||||| 0x00400bb0 bf90384000 mov edi, str.King_:_Congratuations_to_be_a_king ; 0x403890 ; "King : Congratuations to be a king!"
| |||||| 0x00400bb5 e883ffffff call sub.puts_b3d
| |||||| 0x00400bba bfb4384000 mov edi, str.bin_cat_._flag ; 0x4038b4 ; "/bin/cat ./flag" ; const char *string
| |||||| 0x00400bbf e89cfcffff call sym.imp.system ; int system(const char *string)
| ,=======< 0x00400bc4 eb0a jmp 0x400bd0
| ||||||| ; CODE XREFS from sub.puts_b58 (0x400b82, 0x400b8d, 0x400b98, 0x400ba3, 0x400bae)
| |`````--> 0x00400bc6 bfc8384000 mov edi, str.King_:_But_you_couldn_t_make_the_points..._You_can_t_be_a_king. ; 0x4038c8 ; "King : But you couldn't make the points... You can't be a king."
| | | 0x00400bcb e86dffffff call sub.puts_b3d
| | | ; CODE XREFS from sub.puts_b58 (0x400b77, 0x400bc4)
| `-----`-> 0x00400bd0 bfffffffff mov edi, 0xffffffff ; -1 ; int status
\ 0x00400bd5 e816fdffff call sym.imp.exit ; void exit(int status)
Further inspecting the binary we can find the call to scanf
, which reads the key:
[0x00400920]> pdf @ sub.You_:_Am_I...._50d
/ (fcn) sub.You_:_Am_I...._50d 250
| sub.You_:_Am_I...._50d ();
| ; var int local_10h @ rbp-0x10
| ; var int canary @ rbp-0x8
| ; CALL XREF from sub.You_:_There_s_a_book._Let_s_loot_at_it._607 (0x40362e)
| ; CALL XREF from sub.Servant_:_You_are_the_7th_prince_of_this_country._636 (0x403649)
| 0x0040350d 55 push rbp
| ; CODE XREF from fcn.0040341d (+0x7c)
| 0x0040350e 4889e5 mov rbp, rsp
| 0x00403511 4883ec10 sub rsp, 0x10
| 0x00403515 64488b042528. mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
| ; CODE XREF from fcn.0040341d (+0xa9)
| 0x0040351e 488945f8 mov qword [canary], rax
| 0x00403522 31c0 xor eax, eax
| 0x00403524 bf885a4000 mov edi, str.You_:_Am_I.... ; 0x405a88 ; "You : Am I....??"
| 0x00403529 e80fd6ffff call sub.puts_b3d
| 0x0040352e bfa05a4000 mov edi, str.Servant_:_The_king_calls_the_prince ; 0x405aa0 ; "Servant : The king calls the prince!"
| 0x00403533 e805d6ffff call sub.puts_b3d
| 0x00403538 bfc55a4000 mov edi, 0x405ac5 ; "..."
| 0x0040353d e8fbd5ffff call sub.puts_b3d
| 0x00403542 bfc95a4000 mov edi, str...... ; 0x405ac9 ; "....."
| 0x00403547 e8f1d5ffff call sub.puts_b3d
| 0x0040354c bfcf5a4000 mov edi, 0x405acf ; ".......\n"
| 0x00403551 e8e7d5ffff call sub.puts_b3d
| 0x00403556 bfd85a4000 mov edi, str.King_:_I_m_too_old_to_rule_the_kingdom. ; 0x405ad8 ; "King : I'm too old to rule the kingdom."
| 0x0040355b e8ddd5ffff call sub.puts_b3d
| 0x00403560 bf005b4000 mov edi, str.King_:_So_I_will_give_you_few_tests_and_choose_the_next_king. ; 0x405b00 ; "King : So I will give you few tests and choose the next king."
| 0x00403565 e8d3d5ffff call sub.puts_b3d
| 0x0040356a bf405b4000 mov edi, str.King_:_The_only_one_prince_who_passes_all_the_tests_can_be_the_king. ; 0x405b40 ; "King : The only one prince who passes all the tests can be the king.\n"
| 0x0040356f e8c9d5ffff call sub.puts_b3d
| 0x00403574 bf885b4000 mov edi, str.King_:_If_you_want_to_participate_at_this_test__Enter_the_key_for_test_1 ; 0x405b88 ; "King : If you want to participate at this test, Enter the key for test 1"
| 0x00403579 e8bfd5ffff call sub.puts_b3d
| 0x0040357e 488d45f0 lea rax, qword [local_10h]
| 0x00403582 4889c6 mov rsi, rax
| 0x00403585 bff9414000 mov edi, 0x4041f9 ; "%s" ; const char *format
| 0x0040358a b800000000 mov eax, 0
| 0x0040358f e83cd3ffff call sym.imp.__isoc99_scanf ; int scanf(const char *format)
| 0x00403594 bf52394000 mov edi, str.e_H_e_J ; 0x403952 ; const char *format
| 0x00403599 b800000000 mov eax, 0
| 0x0040359e e8cdd2ffff call sym.imp.printf ; int printf(const char *format)
| 0x004035a3 488d45f0 lea rax, qword [local_10h]
| 0x004035a7 ba05000000 mov edx, 5
| 0x004035ac be01000000 mov esi, 1
| 0x004035b1 4889c7 mov rdi, rax
| 0x004035b4 e85dd4ffff call sub.key_d_a16
| 0x004035b9 85c0 test eax, eax
| ,=< 0x004035bb 7424 je 0x4035e1
| | 0x004035bd 8b05e53a2000 mov eax, dword [0x006070a8] ; [0x6070a8:4]=240
| | 0x004035c3 488d55f0 lea rdx, qword [local_10h]
| | 0x004035c7 89c6 mov esi, eax
| | 0x004035c9 bf1d344000 mov edi, fcn.0040341d ; 0x40341d ; "9\a\xff\xd6$\u031a\x13$\xc6\v\xdb\b\a\xfd7Igv3l\a\xffv\x94~\xb6\x8c\x84\x1663\x84\xb4\xa0\u0313\xf0~i,O\x9e\u00ba\xb0\x89\x8c\x04\x1563\x84\xa8\xa0\u0313\xf0\xcce,O\x9e\ueeb0\x89\x8c\xaf\x1963\x84\x9c\xa0\u0313\a\xfbv\x98\a\xff\xf5\xd3"
| | 0x004035ce e8e6d4ffff call sub.strlen_ab9
| | 0x004035d3 488d45f0 lea rax, qword [local_10h]
| | 0x004035d7 4889c7 mov rdi, rax
| | 0x004035da e83efeffff call fcn.0040341d
| ,==< 0x004035df eb0f jmp 0x4035f0
| || ; CODE XREF from sub.You_:_Am_I...._50d (0x4035bb)
| |`-> 0x004035e1 be00000000 mov esi, 0
| | 0x004035e6 bf00424000 mov edi, str.King_:_Wrong__Don_t_you_want_to_be_a_king ; 0x404200 ; "King : Wrong! Don't you want to be a king?"
| | 0x004035eb e868d5ffff call sub.puts_b58
| | ; CODE XREF from sub.You_:_Am_I...._50d (0x4035df)
| `--> 0x004035f0 90 nop
| 0x004035f1 488b45f8 mov rax, qword [canary]
| 0x004035f5 644833042528. xor rax, qword fs:[0x28]
| ,=< 0x004035fe 7405 je 0x403605
| | 0x00403600 e84bd2ffff call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
| | ; CODE XREF from sub.You_:_Am_I...._50d (0x4035fe)
| `-> 0x00403605 c9 leave
\ 0x00403606 c3 ret
The also highlighted call to sub.key_d_a16
compares the entered key with a file called key1
stored on the server:
[0x00400920]> pdf @ sub.key_d_a16
/ (fcn) sub.key_d_a16 163
| sub.key_d_a16 (char *arg1, int arg2, file*arg3);
| ; var file*size @ rbp-0x50
| ; var int local_4ch @ rbp-0x4c
| ; var char *s2 @ rbp-0x48
| ; var file*stream @ rbp-0x38
| ; var char *filename @ rbp-0x30
| ; var char *s1 @ rbp-0x20
| ; var int canary @ rbp-0x8
| ; arg char *arg1 @ rdi
| ; arg int arg2 @ rsi
| ; arg file*arg3 @ rdx
| ; CALL XREF from sub.SYSTEM_:_Break_time_is_ended._2ec (0x40140f)
| ; CALL XREF from sub.SYSTEM_:_Break_time_is_ended._bfa (0x401d1d)
| ; CALL XREF from sub.SYSTEM_:_Break_time_is_ended._26d (0x402390)
| ; CALL XREF from sub.SYSTEM_:_Break_time_is_ended._e4f (0x402f7c)
| ; CALL XREF from sub.You_:_Am_I...._50d (0x4035b4)
| 0x00400a16 55 push rbp
| 0x00400a17 4889e5 mov rbp, rsp
| 0x00400a1a 4883ec50 sub rsp, 0x50 ; 'P'
| 0x00400a1e 48897db8 mov qword [s2], rdi ; arg1
| 0x00400a22 8975b4 mov dword [local_4ch], esi ; arg2
| 0x00400a25 8955b0 mov dword [size], edx ; arg3
| 0x00400a28 64488b042528. mov rax, qword fs:[0x28] ; [0x28:8]=-1 ; '(' ; 40
| 0x00400a31 488945f8 mov qword [canary], rax
| 0x00400a35 31c0 xor eax, eax
| 0x00400a37 8b55b4 mov edx, dword [local_4ch]
| 0x00400a3a 488d45d0 lea rax, qword [filename]
| 0x00400a3e be88384000 mov esi, str.key_d ; 0x403888 ; "key%d" ; const char *format
| 0x00400a43 4889c7 mov rdi, rax ; char *s
| 0x00400a46 b800000000 mov eax, 0
| 0x00400a4b e890feffff call sym.imp.sprintf ; int sprintf(char *s, const char *format, ...)
| 0x00400a50 488d45d0 lea rax, qword [filename]
| 0x00400a54 be8e384000 mov esi, 0x40388e ; "r" ; const char *mode
| 0x00400a59 4889c7 mov rdi, rax ; const char *filename
| 0x00400a5c e85ffeffff call sym.imp.fopen ; file*fopen(const char *filename, const char *mode)
| 0x00400a61 488945c8 mov qword [stream], rax
| 0x00400a65 488b55c8 mov rdx, qword [stream] ; FILE *stream
| 0x00400a69 8b4db0 mov ecx, dword [size]
| 0x00400a6c 488d45e0 lea rax, qword [s1]
| 0x00400a70 89ce mov esi, ecx ; int size
| 0x00400a72 4889c7 mov rdi, rax ; char *s
| 0x00400a75 e816feffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream)
| 0x00400a7a 8b45b0 mov eax, dword [size]
| 0x00400a7d 4863d0 movsxd rdx, eax ; size_t n
| 0x00400a80 488b4db8 mov rcx, qword [s2]
| 0x00400a84 488d45e0 lea rax, qword [s1]
| 0x00400a88 4889ce mov rsi, rcx ; const char *s2
| 0x00400a8b 4889c7 mov rdi, rax ; const char *s1
| 0x00400a8e e88dfdffff call sym.imp.strncmp ; int strncmp(const char *s1, const char *s2, size_t n)
| 0x00400a93 85c0 test eax, eax
| ,=< 0x00400a95 7507 jne 0x400a9e
| | 0x00400a97 b801000000 mov eax, 1
| ,==< 0x00400a9c eb05 jmp 0x400aa3
| || ; CODE XREF from sub.key_d_a16 (0x400a95)
| |`-> 0x00400a9e b800000000 mov eax, 0
| | ; CODE XREF from sub.key_d_a16 (0x400a9c)
| `--> 0x00400aa3 488b4df8 mov rcx, qword [canary]
| 0x00400aa7 6448330c2528. xor rcx, qword fs:[0x28]
| ,=< 0x00400ab0 7405 je 0x400ab7
| | 0x00400ab2 e899fdffff call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
| | ; CODE XREF from sub.key_d_a16 (0x400ab0)
| `-> 0x00400ab7 c9 leave
\ 0x00400ab8 c3 ret
Further on the key stored at local_10h
is passed as an argument to sub.strlen_ab9
:
...
| | 0x004035bd 8b05e53a2000 mov eax, dword [0x006070a8] ; [0x6070a8:4]=240
| | 0x004035c3 488d55f0 lea rdx, qword [local_10h]
| | 0x004035c7 89c6 mov esi, eax
| | 0x004035c9 bf1d344000 mov edi, fcn.0040341d ; 0x40341d ; "9\a\xff\xd6$\u031a\x13$\xc6\v\xdb\b\a\xfd7Igv3l\a\xffv\x94~\xb6\x8c\x84\x1663\x84\xb4\xa0\u0313\xf0~i,O\x9e\u00ba\xb0\x89\x8c\x04\x1563\x84\xa8\xa0\u0313\xf0\xcce,O\x9e\ueeb0\x89\x8c\xaf\x1963\x84\x9c\xa0\u0313\a\xfbv\x98\a\xff\xf5\xd3"
| | 0x004035ce e8e6d4ffff call sub.strlen_ab9
...
The argument passed in edi
(fcn.0040341d
) seems to contain a gibberish byte sequence (see above). A few instructions ahead this address (fcn.0040341d
) is actually called as a function:
...
| | 0x004035d3 488d45f0 lea rax, qword [local_10h]
| | 0x004035d7 4889c7 mov rdi, rax
| | 0x004035da e83efeffff call fcn.0040341d
...
Before this call is made, the function sub.strlen_ab9
XORs the gibberish bytes at fcn.0040341d
with the key:
[0x00400920]> pdf @ sub.strlen_ab9
/ (fcn) sub.strlen_ab9 132
| sub.strlen_ab9 (int arg1, int arg2, char *arg3);
| ; var char *s @ rbp-0x28
| ; var int local_1ch @ rbp-0x1c
| ; var int local_18h @ rbp-0x18
| ; var int local_10h @ rbp-0x10
| ; var size_t local_ch @ rbp-0xc
| ; var unsigned int local_8h @ rbp-0x8
| ; arg int arg1 @ rdi
| ; arg int arg2 @ rsi
| ; arg char *arg3 @ rdx
| ; CALL XREF from sub.SYSTEM_:_Break_time_is_ended._2ec (0x401429)
| ; CALL XREF from sub.SYSTEM_:_Break_time_is_ended._bfa (0x401d37)
| ; CALL XREF from sub.SYSTEM_:_Break_time_is_ended._26d (0x4023aa)
| ; CALL XREF from sub.SYSTEM_:_Break_time_is_ended._e4f (0x402f96)
| ; CALL XREF from sub.You_:_Am_I...._50d (0x4035ce)
| 0x00400ab9 55 push rbp
| 0x00400aba 4889e5 mov rbp, rsp
| 0x00400abd 4883ec30 sub rsp, 0x30 ; '0'
| 0x00400ac1 48897de8 mov qword [local_18h], rdi ; arg1
| 0x00400ac5 8975e4 mov dword [local_1ch], esi ; arg2
| 0x00400ac8 488955d8 mov qword [s], rdx ; arg3
| 0x00400acc c745f0000000. mov dword [local_10h], 0
| 0x00400ad3 488b45d8 mov rax, qword [s]
| 0x00400ad7 4889c7 mov rdi, rax ; const char *s
| 0x00400ada e861fdffff call sym.imp.strlen ; size_t strlen(const char *s)
| 0x00400adf 8945f4 mov dword [local_ch], eax
| 0x00400ae2 488b45e8 mov rax, qword [local_18h]
| 0x00400ae6 488945f8 mov qword [local_8h], rax
| ,=< 0x00400aea eb3b jmp 0x400b27
| | ; CODE XREF from sub.strlen_ab9 (0x400b38)
| .--> 0x00400aec 488b45f8 mov rax, qword [local_8h]
| : | 0x00400af0 0fb600 movzx eax, byte [rax]
| : | 0x00400af3 89c1 mov ecx, eax
| : | 0x00400af5 8b45f0 mov eax, dword [local_10h]
| : | 0x00400af8 4863d0 movsxd rdx, eax
| : | 0x00400afb 488b45d8 mov rax, qword [s]
| : | 0x00400aff 4801d0 add rax, rdx ; '('
| : | 0x00400b02 0fb600 movzx eax, byte [rax]
| : | 0x00400b05 31c8 xor eax, ecx
| : | 0x00400b07 89c2 mov edx, eax
| : | 0x00400b09 488b45f8 mov rax, qword [local_8h]
| : | 0x00400b0d 8810 mov byte [rax], dl
| : | 0x00400b0f 8b45f0 mov eax, dword [local_10h]
| : | 0x00400b12 83c001 add eax, 1
| : | 0x00400b15 ba00000000 mov edx, 0
| : | 0x00400b1a f775f4 div dword [local_ch]
| : | 0x00400b1d 89d0 mov eax, edx
| : | 0x00400b1f 8945f0 mov dword [local_10h], eax
| : | 0x00400b22 488345f801 add qword [local_8h], 1
| : | ; CODE XREF from sub.strlen_ab9 (0x400aea)
| :`-> 0x00400b27 8b45e4 mov eax, dword [local_1ch]
| : 0x00400b2a 4863d0 movsxd rdx, eax
| : 0x00400b2d 488b45e8 mov rax, qword [local_18h]
| : 0x00400b31 4801d0 add rax, rdx ; '('
| : 0x00400b34 483b45f8 cmp rax, qword [local_8h]
| `==< 0x00400b38 77b2 ja 0x400aec
| 0x00400b3a 90 nop
| 0x00400b3b c9 leave
\ 0x00400b3c c3 ret
Since the bytes at fcn.0040341d
are called as a function, the first bytes are supposed to contain a valid function prologue like this:
55 push rbp
4889e5 mov rbp, rsp
4883ec... sub rsp, ...
By XORing the first bytes of fcn.0040341d
with these bytes we can reproduce the key:
root@kali:~# python
...
>>> fcn_0040341d = [0x39, 0x07, 0xff, 0xd6]
>>> prologue = [0x55, 0x48, 0x89, 0xe5]
>>> key = ''
>>> for i in range(len(prologue)):
... key += chr(fcn_0040341d[i]^prologue[i])
...
>>> key
'lOv3'
The first key is lOv3
. Entering this key we can proceed the text adventure (remember that you have to create a file called key1
containing the key to get it working locally):
...
King : If you want to participate at this test, Enter the key for test 1
lOv3
SYSTEM : We will start test 1
King : The bandits are crossing over the northern border. So, go there and defuse the thief.
King : Do you want to go there?
1> I am!
2> No I'm not
1
SYSTEM : You have to pick the Armor
SYSTEM : What do you want?
1> I will choose full armor.
2> I will wear the armor for body, arm, leg and helmet.
2
SYSTEM : You are going to the northern border with other prices.
SYSTEM : You won!
SYSTEM : What will you do with the enemy?
1> Kill the enemy
2> Capture the captive
3> Just release
...
Every now and then our points are displayed:
...
SYSTEM : Your point
brave > 2
wise > 0
kind > 1
decision > 0
sacrifice > 0
...
These attributes are actually the values checked within the function we have already seen printing the flag. Thus our goal is to set all those values to 5 exactly.
In order to reach enough points, we have to determine some more keys:
...
King : Congratuations for passing the first test.
King : I'm very proud of you
King : I will give you a second test.
King : Will you do that?
King : If you want to participate at this test, Enter the key for test 2
To determine this next key I ran the program in gdb and stopped the executing (Ctrl+C
) when the key is supposed to be entered:
root@kali:~/Documents/cg19/kingmaker# gdb ./KingMaker
...
gdb-peda$ r
...
King : Congratuations for passing the first test.
King : I'm very proud of you
King : I will give you a second test.
King : Will you do that?
King : If you want to participate at this test, Enter the key for test 2
^C
Program received signal SIGINT, Interrupt.
[----------------------------------registers-----------------------------------]
RAX: 0xfffffffffffffe00
RBX: 0x7ffff7fb8a00 --> 0xfbad208b
RCX: 0x7ffff7ee77d1 (<__GI___libc_read+17>: cmp rax,0xfffffffffffff000)
RDX: 0x1
RSI: 0x7ffff7fb8a83 --> 0xfba8d0000000000a
RDI: 0x0
RBP: 0xd68 ('h\r')
RSP: 0x7fffffffdb48 --> 0x7ffff7e798b8 (<_IO_new_file_underflow+328>: test rax,rax)
RIP: 0x7ffff7ee77d1 (<__GI___libc_read+17>: cmp rax,0xfffffffffffff000)
R8 : 0x7ffff7fb9760 --> 0xfbad2887
R9 : 0x7ffff7fba8c0 --> 0x0
R10: 0x7ffff7fbf540 (0x00007ffff7fbf540)
R11: 0x246
R12: 0x7ffff7fb4760 --> 0x0
R13: 0x7ffff7fb52a0 --> 0x0
R14: 0x0
R15: 0x1
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x7ffff7ee77cb <__GI___libc_read+11>: jne 0x7ffff7ee77e0 <__GI___libc_read+32>
0x7ffff7ee77cd <__GI___libc_read+13>: xor eax,eax
0x7ffff7ee77cf <__GI___libc_read+15>: syscall
=> 0x7ffff7ee77d1 <__GI___libc_read+17>: cmp rax,0xfffffffffffff000
0x7ffff7ee77d7 <__GI___libc_read+23>: ja 0x7ffff7ee7830 <__GI___libc_read+112>
0x7ffff7ee77d9 <__GI___libc_read+25>: ret
0x7ffff7ee77da <__GI___libc_read+26>: nop WORD PTR [rax+rax*1+0x0]
0x7ffff7ee77e0 <__GI___libc_read+32>: push r12
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdb48 --> 0x7ffff7e798b8 (<_IO_new_file_underflow+328>: test rax,rax)
0008| 0x7fffffffdb50 --> 0x7ffff7fb8a00 --> 0xfbad208b
0016| 0x7fffffffdb58 --> 0x7ffff7fb52a0 --> 0x0
0024| 0x7fffffffdb60 --> 0xffffffffffffff80
0032| 0x7fffffffdb68 --> 0x7ffff7fb8a00 --> 0xfbad208b
0040| 0x7fffffffdb70 --> 0x0
0048| 0x7fffffffdb78 --> 0x7ffff7e7a9d2 (<__GI__IO_default_uflow+50>: cmp eax,0xffffffff)
0056| 0x7fffffffdb80 --> 0xd68 ('h\r')
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGINT
0x00007ffff7ee77d1 in __GI___libc_read (fd=0x0, buf=0x7ffff7fb8a83 <_IO_2_1_stdin_+131>, nbytes=0x1)
at ../sysdeps/unix/sysv/linux/read.c:26
26 ../sysdeps/unix/sysv/linux/read.c: No such file or directory.
gdb-peda$
Now we can jump to the instruction after the scanf
call by entering the gdb command finish
a few times and entering something for the key:
...
[-------------------------------------code-------------------------------------]
0x402f4d: mov edi,0x4041f9
0x402f52: mov eax,0x0
0x402f57: call 0x4008d0 <__isoc99_scanf@plt>
=> 0x402f5c: mov edi,0x403952
0x402f61: mov eax,0x0
0x402f66: call 0x400870 <printf@plt>
0x402f6b: lea rax,[rbp-0x10]
0x402f6f: mov edx,0x5
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe380 --> 0x7fffffffe3b0 --> 0x7fffffffe3d0 --> 0x7fffffffe400 --> 0x7fffffffe430 --> 0x7fffffffe450 (--> ...)
0008| 0x7fffffffe388 --> 0x200400920
0016| 0x7fffffffe390 --> 0x100000000
0024| 0x7fffffffe398 --> 0x0
0032| 0x7fffffffe3a0 --> 0x7fffffff0061 --> 0x0
0040| 0x7fffffffe3a8 --> 0x2257f8e654de900
0048| 0x7fffffffe3b0 --> 0x7fffffffe3d0 --> 0x7fffffffe400 --> 0x7fffffffe430 --> 0x7fffffffe450 --> 0x7fffffffe480 (--> ...)
0056| 0x7fffffffe3b8 --> 0x40305a --> 0x2f883f4458b61eb
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0000000000402f5c in ?? ()
Value returned is $5 = 0x1
gdb-peda$
Since the technique to determine the key is basically the same as the first key, we only need the address where the bytes which are going to be XORed are stored. This address is only a few instructions ahead:
gdb-peda$ x/20i $rip
=> 0x402f5c: mov edi,0x403952
0x402f61: mov eax,0x0
0x402f66: call 0x400870 <printf@plt>
0x402f6b: lea rax,[rbp-0x10]
0x402f6f: mov edx,0x5
0x402f74: mov esi,0x2
0x402f79: mov rdi,rax
0x402f7c: call 0x400a16
0x402f81: test eax,eax
0x402f83: je 0x402fa9
0x402f85: mov eax,DWORD PTR [rip+0x204139] # 0x6070c4
0x402f8b: lea rdx,[rbp-0x10]
0x402f8f: mov esi,eax
0x402f91: mov edi,0x402d55
0x402f96: call 0x400ab9
0x402f9b: lea rax,[rbp-0x10]
0x402f9f: mov rdi,rax
0x402fa2: call 0x402d55
0x402fa7: jmp 0x402fb8
0x402fa9: mov esi,0x0
Now we can also use radare2 to print these bytes in a more comfortable format:
[0x00400920]> pc @ 0x402d55
#define _BUFFER_SIZE 256
const uint8_t buffer[256] = {
0x11, 0x78, 0xe5, 0xd4, 0x0c, 0xb3, 0x80, 0x11, 0x0c, 0xb9,
0x11, 0xd9, 0x20, 0x78, 0xe7, 0x35, 0x61, 0x18, 0x6c, 0x31,
0x44, 0x78, 0xe5, 0x74, 0xbc, 0x01, 0xac, 0x8e, 0x04, 0x66,
...
Again we XOR those bytes to produce a valid function prologue:
root@kali:~# python
...
>>> fcn_402d55 = [0x11, 0x78, 0xe5, 0xd4]
>>> prologue = [0x55, 0x48, 0x89, 0xe5]
>>> key = ''
>>> for i in range(len(prologue)):
... key += chr(fcn_402d55[i]^prologue[i])
...
>>> key
'D0l1'
The second key is D0l1
. Using this key we can proceed with the text adventure and determine the following keys with the same technique. The length of the following keys is longer, but since we already decrypted two functions we can use the necessary amount of bytes of those functions to reproduce the keys. The total number of keys is 5:
lOv3
D0l1
HuNgRYT1m3
F0uRS3aS0n
T1kT4kT0Kk
In the 4th test we have to “decrypt” a message. Here it simply suffices to enter AAAA
for example.
Although we have all keys, we still need to select the correct actions to set all attributes to 5. Only if all attributes equal 5 at the end, we get the flag:
...
King : You passed final test!
King : Congratuations for pass all the tests.
SYSTEM : Your point
brave > 4
wise > 1
kind > 3
decision > 8
sacrifice > 2
King : When you enter that door, read the document with the results written on it.
1> Don't enter the room.
2> Enter the room.
2
SYSTEM : Do you want to read the document?
1> Yes I do.
2> No I don't.
1
You : I will read the document!
King : But you couldn't make the points... You can't be a king.
There are quite a lot of possible combinations of selectable actions. Though it is not the most elegant way, I wrote down all combinations for each level with the corresponding change in the attributes when choosing this combination in the following python script. The script takes those level changes and adds them together in a recursive fashion in order to determine which actions must be selected to gain [5,5,5,5,5]
:
root@kali:~/Documents/cg19/kingmaker# cat solve.py
#!/usr/bin/env python
def addArr(a,b):
c = []
for i in range(5):
c.append(a[i]+b[i])
return c
def addLevel1(a):
x = [("1,lOv3,1,2,1,1", [2,0,1,1,2]),
("1,lOv3,1,2,1,2", [2,-1,0,1,-1]),
("1,lOv3,1,2,1,3", [2,2,0,1,0]),
("1,lOv3,1,2,2,1", [2,0,2,0,2]),
("1,lOv3,1,2,2,2", [2,-1,1,0,-1]),
("1,lOv3,1,2,2,3", [2,2,1,0,0]),
("1,lOv3,1,2,3,1", [2,0,3,1,2]),
("1,lOv3,1,2,3,2", [2,-1,2,1,-1]),
("1,lOv3,1,2,3,3", [2,2,2,1,0])]
for i in range(len(x)):
n = addArr(a,x[i][1])
addLevel2(n,x[i][0])
def addLevel2(a,s):
x = [("D0l1,1,1,1,1,1", [0,1,-1,3,0]),
("D0l1,1,1,1,1,2", [0,1,0,3,0]),
("D0l1,1,1,1,2,1", [0,1,-1,3,0]),
("D0l1,1,1,1,2,2", [0,1,0,3,0]),
("D0l1,1,1,2,1", [0,1,-1,3,0]),
("D0l1,1,1,2,2", [0,1,0,3,0]),
("D0l1,1,2,1,1,1,1,1", [2,2,0,2,0]),
("D0l1,1,2,1,1,1,1,2", [2,2,1,2,0]),
("D0l1,1,2,1,1,1,2,1", [2,2,0,2,0]),
("D0l1,1,2,1,1,1,2,2", [2,2,1,2,0]),
("D0l1,1,2,1,1,2,1", [2,2,1,1,2]),
("D0l1,1,2,1,1,2,2", [2,3,2,1,2]),
("D0l1,1,2,1,2,1,1,1", [2,2,0,2,0]),
("D0l1,1,2,1,2,1,1,2", [2,2,1,2,0]),
("D0l1,1,2,1,2,1,2,1", [2,2,0,2,0]),
("D0l1,1,2,1,2,1,2,2", [2,2,1,2,0]),
("D0l1,1,2,1,2,2,1", [2,2,1,1,2]),
("D0l1,1,2,1,2,2,2", [2,3,2,1,2]),
("D0l1,1,3,1,1,1,1", [2,3,0,2,0]),
("D0l1,1,3,1,1,1,2", [2,3,1,2,0]),
("D0l1,1,3,1,1,2,1", [2,3,0,2,0]),
("D0l1,1,3,1,1,2,2", [2,3,1,2,0]),
("D0l1,1,3,1,2,1", [2,3,1,1,2]),
("D0l1,1,3,1,2,2", [2,4,2,1,2])]
for i in range(len(x)):
n = addArr(a,x[i][1])
addLevel3(n,s+','+x[i][0])
def addLevel3(a, s):
x = [("HuNgRYT1m3,1,1,2", [1,-1,0,3,2]),
("HuNgRYT1m3,1,2,1", [0,0,1,1,0]),
("HuNgRYT1m3,1,2,3", [1,0,1,1,1]),
("HuNgRYT1m3,2,1,2", [1,-2,1,2,2]),
("HuNgRYT1m3,2,2,1", [0,-1,2,0,0]),
("HuNgRYT1m3,2,2,3", [1,-1,2,0,1]),
("HuNgRYT1m3,3,1,2", [1,-2,1,3,2]),
("HuNgRYT1m3,3,2,1", [0,-1,1,1,0]),
("HuNgRYT1m3,3,2,3", [1,-1,1,1,1])]
for i in range(len(x)):
n = addArr(a,x[i][1])
addLevel4(n, s+','+x[i][0])
def addLevel4(a,s):
x = [("F0uRS3aS0n,1,1,AAAA,1,1,2", [0,2,2,3,0]),
("F0uRS3aS0n,1,1,AAAA,1,2", [0,2,1,2,0]),
("F0uRS3aS0n,1,1,AAAA,2", [0,2,1,2,0])]
for i in range(len(x)):
n = addArr(a,x[i][1])
addLevel5(n,s+','+x[i][0])
def addLevel5(a,s):
x = [("T1kT4kT0Kk,2,1,1,2,1", [-1,0,0,1,1]),
("T1kT4kT0Kk,2,1,2,2,1", [0,0,1,2,1]),
("T1kT4kT0Kk,3,2,2,1", [0,0,0,2,2])]
for i in range(len(x)):
n = addArr(a,x[i][1])
for j in range(5):
if (n[j] != 5): break
if (j == 4):
final = s+','+x[i][0]
print(final.replace(',','\n'))
quit()
addLevel1([0,0,0,0,0])
Running the script yields the first valid solution (I determined a total amount of 4 possible solutions):
root@kali:~/Documents/cg19/kingmaker# ./solve.py
1
lOv3
1
2
2
3
D0l1
1
2
1
1
2
1
HuNgRYT1m3
2
2
3
F0uRS3aS0n
1
1
AAAA
1
2
T1kT4kT0Kk
3
2
2
1
We can directly pipe the output of the script to the CTF server to gain the flag:
root@kali:~/Documents/cg19/kingmaker# ./solve.py | nc 110.10.147.104 13152
_ ___ __ __ _
| |/ (_)_ __ __ _| \/ | __ _| | _____ _ __
| ' /| | '_ \ / _` | |\/| |/ _` | |/ / _ \ '__|
| . \| | | | | (_| | | | | (_| | < __/ |
|_|\_\_|_| |_|\__, |_| |_|\__,_|_|\_\___|_|
|___/
Once upon a time, there was a kingdom with 7 princes.
One day the king thinks to decide the who will be the next king.
So he made 5 tests for the princes.
If you pass all the tests, you can be a king!
**********************KING MAKER START**********************
...
King : You passed final test!
King : Congratuations for pass all the tests.
SYSTEM : Your point
brave > 5
wise > 5
kind > 5
decision > 5
sacrifice > 5
King : When you enter that door, read the document with the results written on it.
1> Don't enter the room.
2> Enter the room.
SYSTEM : Do you want to read the document?
1> Yes I do.
2> No I don't.
You : I will read the document!
King : Congratuations to be a king!
He_C@N'T_see_the_f0rest_foR_TH3_TRee$
Done 🙂 The flag is He_C@N'T_see_the_f0rest_foR_TH3_TRee$
.