Codegate CTF 2019 Preliminary – KingMaker

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:

  1. lOv3
  2. D0l1
  3. HuNgRYT1m3
  4. F0uRS3aS0n
  5. 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$.

Leave a Reply

Your email address will not be published. Required fields are marked *