This challenge wasn't solved by me but +winesap during the competition.
I do this challenge off-line and the exploit has been confirmed by +winesap will work.
Basic Info
Attachments are two 32bit ELFs and one plaintext:
note_trial_1,
ptrace_32
,
blacklist.txt
The
note_trial_1
binary is Full RELRO but no PIE enable. While it do have canary, the checksec of pwntools might have bugs.The
ptrace_32
binary is a sandbox, which use ptrace
to forbid its child(i.e note_trial_1
) from using syscalls listed in blacklist.txt
.We will see the forbidden syscalls later, let's focus on functions of
note_trial_1
first.Functions
(Only important functions are listed)
- Initialize notes:
/* struct Note { char name[32]; char date[32]; char body[300]; }; */ for(i = 0; i <= 99; ++i ) { pool[i] = (Note *)malloc(364); memset(pool[i], 0, 364); } /* global variables */ total = 0; cafe = 0xcafebabe;
- Generate license:
unsigned int seed; buf = (unsigned int *)malloc(4u); stream = fopen("/dev/urandom", "rb"); fread(&ptr, 4u, 1u, stream); *buf = ptr; fclose(stream); srand(*buf); for(i = 0; i < 5; i++) license[i] = rand() % 6969 + 1000;
Briefly, reads 4 bytes from/dev/urandom
assrand
seed and generates a five number license. Keep in mind that the seed is recorded on a heap buffer. - add:
note = pool[total++];
readline(note->name, 32)
readline(note->date, 32)
readline(note->body, 300)
- read:
- Input
index
- Show contents of pool[index].
- teencode:
- Input
index
note = pool[index];
- convert content of
note->body to leetcode
(e.g.A->4, C->[, M->|V|
). - license:
return if cafe != 0xcafebabe
- If input the correct five number license, will have format string vulnerability.
cafe = 0xc4f3b4b3
One vulnerability has been described above: if input the correct license then we can use the fmt string attack.
However, we don't know what the correct license is - if we don't know the random seed.
Another vulnerability is the command
teencode
, which converts M
to |V|
, K
to |<
, and thus makes the string longer than buffer size, leads to heap overflow.Exploitation
Exploitation contains two parts, first part is to leak the seed and second part is use fsa to leak information and control
eip
.Leak seed
First part is very easy.
Remember that the seed has been recorded on heap, which located right after the 100th note. So just simply use the command
teencode
to make the 100th note's body "touch" the seed, then read the note will print the seed out. +--------------------------+------------+------+
| ...AAAAAAAAAAMMKFFFF\x00 | 0x00000011 | seed |
+--------------------------+------------+------+
|
v
+--------------------------+------------+------+
| ...4444444444|V||V||< | FFFF | seed |
+--------------------------+------------+------+
Now we have the seed, and we write a simple C code to get the corresponding license.
Second part is the fmt string attacking.
However, there's a global variable
cafe
that make us only have one chance to use fsa. But the solution is easy, use fsa to overwrite cafe
back to 0xcafebabe
every time then we have unlimited times of fsa.Information leak
With fsa, information leak is very easy.
Here we leak the libc, heap and stack address, and use
DynELF
of pwntools to get the remote glibc version.Control EIP
With fsa and stack address, we can overwrite the return address and use ROP to control code flow.
While call
system("/bin/sh")
directly will fail! Yes, the execve
syscall will be caught by the sandbox ptrace_32
.Since there's input length limit of the format string, use it to create ROP chain is inconvenient. We choose to put shellcode on heap and use ROP to call
mprotect(heap, 0x1000, 7)
, then we can execute arbitrary code with (almost) unlimited length.Bypass sandbox
It's time to see what syscalls are forbidden.
The syscall numbers listed in blacklist.txt are
120 (clone) 2 (fork) 190 (vfork) 11 (execve) 37 (kill) 238 (tkill) 270 (tgkill)
We can use
open + getdents + write
to create a /bin/ls
like shellcode. While this will find lots of directories and files exist on remote server, and none of them named "flag". So we choose to bypass the sandbox and get shell to make life easier.Common two strategies to bypass the
ptrace
-base sandbox are:- simply
fork/vfork/clone
- kill parent process
Obviously we cannot use these two strategies.
The other common strategy is to change architecture. Though this binary is 32bit, it can switch to 64bit and use 64bit syscalls.
Change architecture to 64 bit is simple:
push 0x33 call change_to_64
/* switch to 64 bits after retf */ jmp s64 /* same asm between 32 and 64 */ change_to_64: retf s64: /* 64bit shellcode */
Notice that even we switch to 64bit arch, we still cannot use those forbidden syscall numbers. Unfortunately, the syscall number 2 is
open
in 64bit.But this is not a problem, now we can use the normal strategy to escape from our parent - use
fork
.s64: /* 64bit shellcode */ /* call fork() */ push 0x39 pop rax syscall test eax, eax jne exit /* shellcode without sandbox tracing! */ ... exit: /* call exit() */ push 0x3c pop rax syscall
Then we can get shell and find flag in one of lots files ;).
Exploit script
Flag:
WhiteHat{3500ab3a525e759b258585f8494891435b5b16f8}
沒有留言:
張貼留言