|
| 1 | +# Impossible Password |
| 2 | + |
| 3 | +Once extracted, we have the `impossible_password.bin` file. Let's see what type it is. |
| 4 | +``` |
| 5 | +file impossible_password.bin |
| 6 | +impossible_password.bin: 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]=ba116ba1912a8c3779ddeb579404e2fdf34b1568, stripped |
| 7 | +``` |
| 8 | + |
| 9 | +It’s an `ELF 64-bit LSB` executable. |
| 10 | + |
| 11 | +Strings inside the `.data` section |
| 12 | +``` |
| 13 | +rabin2 -z impossible_password.bin |
| 14 | +[Strings] |
| 15 | +nth paddr vaddr len size section type string |
| 16 | +――――――――――――――――――――――――――――――――――――――――――――――――――――――― |
| 17 | +0 0x00000a70 0x00400a70 14 15 .rodata ascii SuperSeKretKey |
| 18 | +1 0x00000a82 0x00400a82 4 5 .rodata ascii %20s |
| 19 | +2 0x00000a87 0x00400a87 5 6 .rodata ascii [%s]\n |
| 20 | +``` |
| 21 | + |
| 22 | +`SuperSeKretKey` appears to be interesting |
| 23 | + |
| 24 | +# Execution |
| 25 | +``` |
| 26 | +chmod +x impossible_password.bin |
| 27 | +./impossible_password.bin |
| 28 | +* SuperSeKretKey |
| 29 | +[SuperSeKretKey] |
| 30 | +** WhatShouldIWrite? |
| 31 | +``` |
| 32 | + |
| 33 | +By executing this program, a star `(*)` is displayed. I want to write `SuperSeKretKey` because the string displayed in the `.data` section informs us that the string (`%s`) is manipulated. Indeed, when I put SuperSeKretKey, this text is surrounded with `[]`. |
| 34 | + |
| 35 | +After this, two stars `(**)` are displayed. I’ve tried to write many things (as WhatShouldIWrite?), but the program always terminates |
| 36 | + |
| 37 | +It looks like the program is waiting for a specific string, but I don’t know what it is. |
| 38 | + |
| 39 | +# Reverse engineering |
| 40 | +<i>Let's use Radare2</i> |
| 41 | + |
| 42 | +Perform analysis : |
| 43 | +``` |
| 44 | +radare2 impossible_password.bin |
| 45 | +[0x004006a0]> aaaa |
| 46 | +[x] Analyze all flags starting with sym. and entry0 (aa) |
| 47 | +[x] Analyze function calls (aac) |
| 48 | +[x] Analyze len bytes of instructions for references (aar) |
| 49 | +[x] Check for objc references |
| 50 | +[x] Check for vtables |
| 51 | +[x] Type matching analysis for all functions (aaft) |
| 52 | +[x] Propagate noreturn information |
| 53 | +[x] Use -AA or aaaa to perform additional experimental analysis. |
| 54 | +[x] Finding function preludes |
| 55 | +[x] Enable constraint types analysis for variables |
| 56 | +``` |
| 57 | +Change memory address to main function : |
| 58 | +``` |
| 59 | +[0x004006a0]> s main |
| 60 | +[0x0040085d]> |
| 61 | +``` |
| 62 | +The focus is now at the `main()` address: `0x0040085d`. |
| 63 | + |
| 64 | +# Disassemble |
| 65 | + |
| 66 | +With the `pdf` command, disassemble the `main()` code |
| 67 | + |
| 68 | +> pdf => `print disassemble function` |
| 69 | +
|
| 70 | +``` |
| 71 | +[0x0040085d]> pdf |
| 72 | + ; DATA XREF from entry0 @ 0x4006bd |
| 73 | +┌ 283: int main (int argc, char **argv); |
| 74 | +│ ; var int64_t var_50h @ rbp-0x50 |
| 75 | +│ ; var int64_t var_44h @ rbp-0x44 |
| 76 | +│ ; var int64_t var_40h @ rbp-0x40 |
| 77 | +│ ; var int64_t var_3fh @ rbp-0x3f |
| 78 | +│ ; var int64_t var_3eh @ rbp-0x3e |
| 79 | +│ ; var int64_t var_3dh @ rbp-0x3d |
| 80 | +│ ; var int64_t var_3ch @ rbp-0x3c |
| 81 | +│ ; var int64_t var_3bh @ rbp-0x3b |
| 82 | +│ ; var int64_t var_3ah @ rbp-0x3a |
| 83 | +│ ; var int64_t var_39h @ rbp-0x39 |
| 84 | +│ ; var int64_t var_38h @ rbp-0x38 |
| 85 | +│ ; var int64_t var_37h @ rbp-0x37 |
| 86 | +│ ; var int64_t var_36h @ rbp-0x36 |
| 87 | +│ ; var int64_t var_35h @ rbp-0x35 |
| 88 | +│ ; var int64_t var_34h @ rbp-0x34 |
| 89 | +│ ; var int64_t var_33h @ rbp-0x33 |
| 90 | +│ ; var int64_t var_32h @ rbp-0x32 |
| 91 | +│ ; var int64_t var_31h @ rbp-0x31 |
| 92 | +│ ; var int64_t var_30h @ rbp-0x30 |
| 93 | +│ ; var int64_t var_2fh @ rbp-0x2f |
| 94 | +│ ; var int64_t var_2eh @ rbp-0x2e |
| 95 | +│ ; var int64_t var_2dh @ rbp-0x2d |
| 96 | +│ ; var int64_t var_20h @ rbp-0x20 |
| 97 | +│ ; var int64_t var_ch @ rbp-0xc |
| 98 | +│ ; var int64_t var_8h @ rbp-0x8 |
| 99 | +│ ; arg int argc @ rdi |
| 100 | +│ ; arg char **argv @ rsi |
| 101 | +│ 0x0040085d 55 push rbp |
| 102 | +│ 0x0040085e 4889e5 mov rbp, rsp |
| 103 | +│ 0x00400861 4883ec50 sub rsp, 0x50 |
| 104 | +│ 0x00400865 897dbc mov dword [var_44h], edi ; argc |
| 105 | +│ 0x00400868 488975b0 mov qword [var_50h], rsi ; argv |
| 106 | +│ 0x0040086c 48c745f8700a. mov qword [var_8h], str.SuperSeKretKey ; 0x400a70 ; "SuperSeKretKey" |
| 107 | +│ 0x00400874 c645c041 mov byte [var_40h], 0x41 ; 'A' ; 65 |
| 108 | +│ 0x00400878 c645c15d mov byte [var_3fh], 0x5d ; ']' ; 93 |
| 109 | +│ 0x0040087c c645c24b mov byte [var_3eh], 0x4b ; 'K' ; 75 |
| 110 | +│ 0x00400880 c645c372 mov byte [var_3dh], 0x72 ; 'r' ; 114 |
| 111 | +│ 0x00400884 c645c43d mov byte [var_3ch], 0x3d ; '=' ; 61 |
| 112 | +│ 0x00400888 c645c539 mov byte [var_3bh], 0x39 ; '9' ; 57 |
| 113 | +│ 0x0040088c c645c66b mov byte [var_3ah], 0x6b ; 'k' ; 107 |
| 114 | +│ 0x00400890 c645c730 mov byte [var_39h], 0x30 ; '0' ; 48 |
| 115 | +│ 0x00400894 c645c83d mov byte [var_38h], 0x3d ; '=' ; 61 |
| 116 | +│ 0x00400898 c645c930 mov byte [var_37h], 0x30 ; '0' ; 48 |
| 117 | +│ 0x0040089c c645ca6f mov byte [var_36h], 0x6f ; 'o' ; 111 |
| 118 | +│ 0x004008a0 c645cb30 mov byte [var_35h], 0x30 ; '0' ; 48 |
| 119 | +│ 0x004008a4 c645cc3b mov byte [var_34h], 0x3b ; ';' ; 59 |
| 120 | +│ 0x004008a8 c645cd6b mov byte [var_33h], 0x6b ; 'k' ; 107 |
| 121 | +│ 0x004008ac c645ce31 mov byte [var_32h], 0x31 ; '1' ; 49 |
| 122 | +│ 0x004008b0 c645cf3f mov byte [var_31h], 0x3f ; '?' ; 63 |
| 123 | +│ 0x004008b4 c645d06b mov byte [var_30h], 0x6b ; 'k' ; 107 |
| 124 | +│ 0x004008b8 c645d138 mov byte [var_2fh], 0x38 ; '8' ; 56 |
| 125 | +│ 0x004008bc c645d231 mov byte [var_2eh], 0x31 ; '1' ; 49 |
| 126 | +│ 0x004008c0 c645d374 mov byte [var_2dh], 0x74 ; 't' ; 116 |
| 127 | +│ 0x004008c4 bf7f0a4000 mov edi, 0x400a7f ; const char *format |
| 128 | +│ 0x004008c9 b800000000 mov eax, 0 |
| 129 | +│ 0x004008ce e82dfdffff call sym.imp.printf ; int printf(const char *format) |
| 130 | +│ 0x004008d3 488d45e0 lea rax, [var_20h] |
| 131 | +│ 0x004008d7 4889c6 mov rsi, rax |
| 132 | +│ 0x004008da bf820a4000 mov edi, str.20s ; 0x400a82 ; "%20s" ; const char *format |
| 133 | +│ 0x004008df b800000000 mov eax, 0 |
| 134 | +│ 0x004008e4 e887fdffff call sym.imp.__isoc99_scanf ; int scanf(const char *format) |
| 135 | +│ 0x004008e9 488d45e0 lea rax, [var_20h] |
| 136 | +│ 0x004008ed 4889c6 mov rsi, rax |
| 137 | +│ 0x004008f0 bf870a4000 mov edi, str.s ; 0x400a87 ; "[%s]\n" ; const char *format |
| 138 | +│ 0x004008f5 b800000000 mov eax, 0 |
| 139 | +│ 0x004008fa e801fdffff call sym.imp.printf ; int printf(const char *format) |
| 140 | +│ 0x004008ff 488b55f8 mov rdx, qword [var_8h] |
| 141 | +│ 0x00400903 488d45e0 lea rax, [var_20h] |
| 142 | +│ 0x00400907 4889d6 mov rsi, rdx ; const char *s2 |
| 143 | +│ 0x0040090a 4889c7 mov rdi, rax ; const char *s1 |
| 144 | +│ 0x0040090d e81efdffff call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2) |
| 145 | +│ 0x00400912 8945f4 mov dword [var_ch], eax |
| 146 | +│ 0x00400915 837df400 cmp dword [var_ch], 0 |
| 147 | +│ ┌─< 0x00400919 740a je 0x400925 |
| 148 | +│ │ 0x0040091b bf01000000 mov edi, 1 ; int status |
| 149 | +│ │ 0x00400920 e85bfdffff call sym.imp.exit ; void exit(int status) |
| 150 | +│ │ ; CODE XREF from main @ 0x400919 |
| 151 | +│ └─> 0x00400925 bf8d0a4000 mov edi, 0x400a8d ; const char *format |
| 152 | +│ 0x0040092a b800000000 mov eax, 0 |
| 153 | +│ 0x0040092f e8ccfcffff call sym.imp.printf ; int printf(const char *format) |
| 154 | +│ 0x00400934 488d45e0 lea rax, [var_20h] |
| 155 | +│ 0x00400938 4889c6 mov rsi, rax |
| 156 | +│ 0x0040093b bf820a4000 mov edi, str.20s ; 0x400a82 ; "%20s" ; const char *format |
| 157 | +│ 0x00400940 b800000000 mov eax, 0 |
| 158 | +│ 0x00400945 e826fdffff call sym.imp.__isoc99_scanf ; int scanf(const char *format) |
| 159 | +│ 0x0040094a bf14000000 mov edi, 0x14 ; 20 ; size_t arg1 |
| 160 | +│ 0x0040094f e839feffff call fcn.0040078d |
| 161 | +│ 0x00400954 4889c2 mov rdx, rax |
| 162 | +│ 0x00400957 488d45e0 lea rax, [var_20h] |
| 163 | +│ 0x0040095b 4889d6 mov rsi, rdx ; const char *s2 |
| 164 | +│ 0x0040095e 4889c7 mov rdi, rax ; const char *s1 |
| 165 | +│ 0x00400961 e8cafcffff call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2) |
| 166 | +│ 0x00400966 85c0 test eax, eax |
| 167 | +│ ┌─< 0x00400968 750c jne 0x400976 |
| 168 | +│ │ 0x0040096a 488d45c0 lea rax, [var_40h] |
| 169 | +│ │ 0x0040096e 4889c7 mov rdi, rax ; int64_t arg1 |
| 170 | +│ │ 0x00400971 e802000000 call fcn.00400978 |
| 171 | +│ │ ; CODE XREF from main @ 0x400968 |
| 172 | +│ └─> 0x00400976 c9 leave |
| 173 | +└ 0x00400977 c3 ret |
| 174 | +``` |
| 175 | + |
| 176 | +From `0x0040086c` to `0x00400920` memory addresses, some manipulations are carried out on strings and to be honest, I didn’t try to understand because something popped into my head. |
| 177 | + |
| 178 | +At `0x00400961` memory address, the binary safe string comparison strcmp is performed between `*s1` and `*`s2`. |
| 179 | + |
| 180 | +Let’s talk about the `0x00400968` memory address. |
| 181 | + |
| 182 | +Register `eax` will contain the return code from `strcmp`, after the call. The test `eax`, `eax` is the same as `and` `eax`, `eax` (bitwise and) except that it doesn’t store the result in `eax`. So `eax` isn’t affected by the test, but the `zero-flag (ZF)` is. |
| 183 | + |
| 184 | +The test `eax`, `eax` is necessary to make the jne work in the first place. Also, jne is the same as `jnz`, just as `je` is the same as `jz`. Both act based on the `ZF` value. |
| 185 | + |
| 186 | +The `jne` branch will be taken if `ZF=0` and therefore whenever `strcmp` returns a non-zero value (strings not equal). Conversely if `eax` contains zero upon return from `strcmp`, the jump via `jne` will not happen. |
| 187 | + |
| 188 | +If you have understood everything correctly, `strcmp` compares the strings and sets `eax` to zero if the strings are `equal`. If they are not, the `jne` instruction takes us to the memory address `0x00400976` which is the program’s exit (leave). |
| 189 | + |
| 190 | +# Reopen in read-write |
| 191 | +``` |
| 192 | +[0x0040085d]> oo+ |
| 193 | +``` |
| 194 | +Change memory address focus |
| 195 | + |
| 196 | +We want to edit the `jne` section, so let’s jump into this memory address. |
| 197 | +``` |
| 198 | +Change memory address focus |
| 199 | +We want to edit the jne section, so let’s jump into this memory address. |
| 200 | +``` |
| 201 | +ASM instruction modification |
| 202 | + |
| 203 | +The easy way to bypass this `jne` is to write `NOP` (`No OPeration`) instruction. |
| 204 | +``` |
| 205 | +[0x00400968]> wx 9090 |
| 206 | +[0x00400968]> wa nop |
| 207 | +Written 1 byte(s) (nop) = wx 90 |
| 208 | +``` |
| 209 | +We can see our new instruction by disassembling a new time (the disassemble has been truncated). |
| 210 | +``` |
| 211 | +[0x00400968]> pdf |
| 212 | +0x00400961 e8cafcffff call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2) |
| 213 | +0x00400966 85c0 test eax, eax |
| 214 | +0x00400968 90 nop |
| 215 | +``` |
| 216 | +That’s all with `radare2`, we can leave. |
| 217 | +``` |
| 218 | +[0x00400968]> q |
| 219 | +``` |
| 220 | +Execution |
| 221 | +``` |
| 222 | +./impossible_password.bin |
| 223 | +* SuperSeKretKey |
| 224 | +[SuperSeKretKey] |
| 225 | +** plop |
| 226 | +HTB{+*+*+*+*+*+*+*+*+*+*+*+*} |
| 227 | +``` |
| 228 | +Affected by: [Adrien](https://illuad.fr/2020/07/16/writeup-htb-reversing-impossible-password.html) |
0 commit comments