✅Jarvis OJ Pwn Xman Series
Xman Level 0 (ret2text)
file + checksec
file:
checksec:
Program Analysis
Examine the main function:
vulnerable_function() has stack overflow vulnerability:
callsystem() is able to spawn a shell:
Solution
The function vulnerable_function() is called and it is vulnerable to buffer overflow attack. The buffer buf is only 0x80 bytes long but we are able to write 0x200 bytes into it at read(0, &buf, 0x200uLL);. In the binary we can find a "backdoor" function named callsystem(). Here we should overflow the buffer, control the instruction pointer, and then use ret2text to redirect the control flow to callsystem().
ret2text is possible when there exist dead code in the program. "Dead code" refers to a piece of code that never gets used by the program. This happens because developer may forget which function is neccessary and which function is useless during the developement process. Also, sometimes the developer would insert some kind of "backdoor" in the project for convenience, and this makes attack convenient in the meantime.
Exploit
Xman Level 1 (ret2shellcode)
file + checksec
file:
checksec:
Program Analysis
Examine the main function:
vulnerable_function() has stack overflow vulnerability:
Solution
The absence of NX makes this binary vulnerable to ret2shellcode. Since we are allowed to write 0x100 bytes into buf, the pwntools' built-in shellcode suffices.
Exploit
Xman Level 2 32-bit (ret2system)
file + checksec
file:
checksec:
Program Analysis
Examine the main function:
vulnerable_function() has stack overflow vulnerability:
Solution
Since NX is enabled, we can't do ret2shellcode this time because the shellcode stored on stack won't be executed. Instead, we use ret2system since it is one of the standard methods for bypassing NX. Note that both system and /bin/sh are provided in the binary:
Hence we can call system("/bin/sh") directly. This is the easiest type of libc.
Exploit
Xman Level 2 64-bit (64-bit Calling Convention)
file + checksec
file:
checksec:
Program Analysis
Examine the main function:
vulnerable_function() has stack overflow vulnerability:
Solution
This time we are dealing with x64 architecture. The major distinction between x86 and x64 is different calling conventions. In x86, the function arguments are stored on the stack. In x64, the first 6 function arguments are stored in registers, in the following order:
RDI = arg1
RSI = arg2
RDX = arg3
R10 = arg4 (R10 for kernel space and RCX for user space. We are interested in kernel space here.)
R8 = arg5
R9 = arg6
If there exists more arguments, the extra ones will be stored on the stack.
To pass /bin/sh as the argument for system, we need to store /bin/sh in rdi. This can be done with the pop rdi gadget:
Exploit
Xman Level 3 32-bit (ret2libc)
file + checksec
file:
checksec:
Program Analysis
Examine the main function:
vulnerable_function() has stack overflow vulnerability:
Solution
No more system provided in binary this time, so we need to leak an address (write_got in this case) from the GOT table and then calculate the libc base address based on this leaked address. Once we have the libc base address, we are able to deduce the addresses of system and /bin/sh in libc.
The candidates of this leaking phase include puts, write or printf. They will be called ret2puts, ret2write and ret2printf, respectively. Usually we want to do ret2puts, but since there is no puts@plt or printf@plt in this binary, the only choice left for us is ret2write.
Exploit
Xman Level 3 64-bit (ret2libc)
file + checksec
file:
checksec:
Program Analysis
Examine the main function:
vulnerable_function() has stack overflow vulnerability:
Solution
We need gadgets pop rdi, pop rsi and pop rdx this time. We can find pop rdi ; ret in the binary:
However, we can't find an independent gadget like pop rsi; ret. The good news is pop rsi ; pop r15 ; ret could be used as an alternative:
Here we simply pass a junk value into r15, so this gadget would do the same job as pop rsi ; ret.
We still need pop rdx ; ret. However, this gadget is not present in the binary. It doesn't really matter because the value stored in rdx is greater than 6 at the moment write gets called. This is just what we want since the address of write@GOT won't be longer than 6 bytes. As a result, we don't have to set the value of rdx on ourselves, so just ignore it.
Exploit
Xman Level 4 (Libc Database)
file + checksec
file:
checksec:
Program Analysis
Examine the main function:
vulnerable_function() has stack overflow vulnerability:
Solution
The libc file is not given this time, but that's not a problem. We can always query the leaked address from libc database and figure out the libc version as well as the corresponding libc function offsets (relative to the libc base address).
Exploit
Xman Level 5 (mprotect)
file + checksec
file:
checksec:
Pseudocode
Examine the main function:
vulnerable_function() has stack overflow vulnerability:
Solution
In this challenge, system and execve are disabled (at least we pretend that they are disabled) and we are supposed to use mmap or mprotect. Using mprotect is the easier route. The exploit splits into three phases:
Leak the address of
write_got, calculate libc base address and then deduce the address ofmprotect.Call
mprotectto give the.bsssegmentrwxpermission.Call
readto start a stdin session and input our shellcode to the.bsssegment. Set the return address ofreadto be the address of.bsssegment so the shellcode gets triggered.
Phase 1 is essentially the same as Level 3 (x64).
Phase 2 is something new. Here we want to call mprotect(void *addr, size_t len, int prot), where:
addris the address of the buffer.lenis the length of the buffer. Say it is0x1000, which is more than enough.protis the permission that we want that buffer to have, which is7 = 0b111 = rwxin this case.
Phase 3 is a slightly advanced version of ret2shellcode. Here we use multi-stage shellcode. In stage 1, we call the read() function to open a STDIN session. In stage 2, we input the /bin/sh shellcode from STDIN, and get shell.
In stage 1, we use ROP to call read(int fd, void *buf, size_t nbyte), where:
fdshould be 0 since we want stdin.bufis the address of the buffer. We will useelf.bss()here, which is the beginning of the.bsssegment.nbyteis the length of our input. Let's say it's0x100, which is more than enough.
In stage 2, we can input our shellcode from STDIN. If the return address of read is set to be elf.bss(), the shellcode will be triggered and we would get shell.
Exploit
Xman Level 6
Todo!
Last updated