By using the so called universal gadget from __libc_csu_init
we can read shellcode into the rwx memory segment and return into it.
By executing read function we can overwrite only last two bytes of read to find something useful and defeat ASLR. Fortunately there is one-gadget RCE located at 0xf0567
in this version of libc, right near the read
function (0xf6670). We overflow only last two bytes to defeat ASLR, so that only around 16 attemps needed, because of 4 bit entropy of ASLR.
EDIT: checkout another great solution proposed by agadeint in the comment section below, which is cleaner and does not require bruteforcing and one gadget.
Stack looks like this:
...
char buf[100];
char *buf_;
gets function is used to fill the buf
, so it's easy to overflow buf_
pointer, which is later passed into count_crc
function, so we can count crc almost of any memory region, thus we can get arbitrary read. We can get base address of libc from .got, and also we can get canary, because it's located in the memory segment right after the ld.so, which we can get by using kind of offset2ldso attack (though it is not stable and platform specific).
We can underflow the fu
pointer which in located in .bss section and make it point into the .got section, where we can read and write. I address of libc from got, then changed strlen@got
to address of system@libc
and puts@got
into the address of main function, so after restart of the binary system
function will be called instead of strlen
of string controlled by me.
First of all canary can be leaked by entering numbers 1 .. 7 into the menu and the last byte is always zero. Also in this binary couple of very useful gadgets like syscall; ret
can be found. So by using these gadgets and technic called SROP we can leak .got section, read our new ropchain and then return on it by using sigreturn.
In this task we can read any file, so we can read /proc/self/maps
to defeat ASLR and by abusing double free into fast bin attack we can overflow __malloc_hook
by address of system and call malloc(addr_of_binsh_on_heap)
, so system("/bin/sh")
will be called.
In this task we can not read any file on the system, but we can get memory leak of heap by reading contents of fastbin chunk, and we can get memory leak of libc arena by reading contents of small chunk. Then I overflow __malloc_hook
once again into the one-gadget RCE and trigger double free error, which will trigger our hook. This is done exactly that way, because there is not such one-gadget that meets contrains if you simply call malloc, and you can not do the trick with addr_of_binsh_on_heap
like the last time, because of the PIE address of the heap is now more that 2 ** 32
and we could only call malloc(unsigned int size)
.
defaulter says he accepts a shellcode as input. But obviously was some trick in this task. First I tried to call execve("/bin/sh", NULL, NULL)
shellcode, just to be sure that it blocked by seccomp sandbox or some sort of it (and it didn't work). So I tried testing over syscalls and I jumped into the inf. loop if syscall fails. open
syscall was blocked as well, but openat
was not, but openat
needs absolute path to file as argument so I read the /etc/passwd
to get username (which was pwn) and the read /home/pwn/flag
. So full path looks like this: openat flag, read the flag into writable location, write it to the screen.
We used read to setup some necessary rop chain stuff before and after the got, then pivoted esp into the got. Next we used pop ebp and push ebp gadgets in start_hard to make a copy of the got read pointer to a location after the got. One last use of read to flip the last byte of read to 0xd0 which turned it into write. Now we had read and write so game over.