
서버IP로 접속하면 Apache 디폴트 페이지가 우리를 맞이한다.

nmap 스캔 결과 22번 포트와 80번 포트가 열려있다.

디렉토리 버스팅 결과 /app이 보인다.

pluck-4.7.13 디렉토리를 클릭하면 위와 같이 dreaming 페이지를 확인할 수 있다.

로그인 페이지에서 테스트 겸 'password'를 입력했더니 로그인이 되었다.

php 리버스쉘을 업로드했는데 텍스트 파일로 변경되어버렸다.
구글링 결과 pluck 4.7.13은 파일 업로드 제한 우회 취약점이 있는 버전이었고 .phar 확장자로 우회가 가능했다.

리버스쉘을 통해 침투 성공!

.htaccess 파일을 확인해보면 php로 시작하는 확장자와 pthml 확장자의 핸들러를 제거하여 텍스트 파일로 처리되게 설정되어있다.
phar 확장자의 핸들러는 제거되지 않았기 때문에 파일 업로드 우회가 가능했던 것이었다.

사용자 계정은 lucien, death, morpheus 3개가 있는 것을 확인했다.

서버를 뒤져보던 중 /opt/test.py 코드에서 lucien 계정의 패스워드를 확인할 수 있었다.
lucien 홈디렉토리에서 첫번째 플래그 획득!
lucien 홈디렉토리의 .bash_history 파일을 살펴보면 몇가지 시선을 끄는 부분이 있다.
첫번째,
cd usr
cd lib
cd python3.8
nano shutil.py
python 라이브러리를 직접 생성한 것 같다.

ls 명령으로 확인해봤는데 대부분 라이브러리 파일들은 소유자와 소유그룹이 root인데 특이하게 shutil.py 파일은 소유그룹이 death 이다. 심지어 수정권한도 있다.
지금은 쓸모없지만 나중에 death 계정으로 접속하면 써먹을 곳이 있을 것 같다.
두번째,
mysql -u lucien -plucien42DBPASSWORD
mysql 접속명령어인데 일반적으로는 -p 옵션만 사용한뒤 프롬프트를 통해 패스워드를 입력하지만 위처럼 -p 옵션 뒤에 바로 패스워드를 입력할 수도 있다.
다만 이렇게 히스토리 파일 등으로 패스워드가 유출될 수 있기에 보안상 취약하다.
세번째,
sudo -l
/usr/bin/python3 /home/death/getDreams.py
sudo -u death /usr/bin/python3 /home/death/getDreams.py
sudo를 통해 명령을 실행한 것 같다.

'sudo -l'로 확인했더니 death 계정 권한으로 패스워드 없이 아래 명령을 실행할 수 있다.
sudo /usr/bin/python3 /home/death/getDreams.py

명령을 실행했더니 알 수 없는 문자열이 출력됐다.
/home/death/getDreams.py 내용은 권한이 없어서 못보지만 아까 /opt 폴더에 똑같은 이름의 파일이 있는 것을 확인했었다.
import mysql.connector
import subprocess
# MySQL credentials
DB_USER = "death"
DB_PASS = "#redacted"
DB_NAME = "library"
import mysql.connector
import subprocess
def getDreams():
try:
# Connect to the MySQL database
connection = mysql.connector.connect(
host="localhost",
user=DB_USER,
password=DB_PASS,
database=DB_NAME
)
# Create a cursor object to execute SQL queries
cursor = connection.cursor()
# Construct the MySQL query to fetch dreamer and dream columns from dreams table
query = "SELECT dreamer, dream FROM dreams;"
# Execute the query
cursor.execute(query)
# Fetch all the dreamer and dream information
dreams_info = cursor.fetchall()
if not dreams_info:
print("No dreams found in the database.")
else:
# Loop through the results and echo the information using subprocess
for dream_info in dreams_info:
dreamer, dream = dream_info
command = f"echo {dreamer} + {dream}"
shell = subprocess.check_output(command, text=True, shell=True)
print(shell)
except mysql.connector.Error as error:
# Handle any errors that might occur during the database connection or query execution
print(f"Error: {error}")
finally:
# Close the cursor and connection
cursor.close()
connection.close()
# Call the function to echo the dreamer and dream information
getDreams()
death 계정으로 mysql에 접속하여 dreams 테이블에 있는 dreamer와 dream 데이터를 가져와 echo 명령을 통해 콘솔에 출력하는 코드이다.
우리는 mysql에 접속할 수 있는 lucien 계정의 패스워드를 알고 있으니 dreams 테이블의 데이터를 조작할 수 있을 것 같다.
DB테이블을 조작하는 방법을 몰라도 lucien 홈디렉토리에 있는 .mysql_history에서 힌트를 얻을 수 있다.

dream 컬럼에서 'Flying in the sky'를 '; /bin/bash'로 변경했다.
이제 sudo 명령을 실행하면 death 계정 권한으로 bash가 실행된다.

성공!

하지만 interactive한 쉘이 아니어서 콘솔에 아무것도 출력되지 않는다.

death계정 쉘을 종료했을 때 실행결과가 한꺼번에 출력되는 것을 이용해서 death 홈디렉토리의 getDream.py 파일을 읽은 뒤 쉘을 종료했다.
DB_PASS에 death 계정 패스워드가 하드코딩 되어있다.
이 패스워드를 사용하여 su 명령을 통해 정상적으로 death 계정에 접속할 수 있다.
이제 death 홈디렉토리에 있는 두번째 플래그를 제출하면 된다.

death 홈디렉토리를 뒤져봤지만 별게 없었고 morpheus의 홈디렉토리에 있는 restore.py 파일을 확인해봤는데 익숙한 라이브러리가 보인다.
lucien 계정의 bash_history에서 봤던 shutil.py 라이브러리이다.
우리는 아까 death 계정에 shutil.py 파일 수정 권한이 있다는 것을 알고 있다.
파일을 우리가 원하는 코드로 바꿔놓고 restore.py를 실행하면 된다.
하지만 우리에게 restore.py 실행권한이 없다.

pspy를 통해 예약작업이 있는지 확인해보니 morpheus의 uid로 1분마다 restore.py가 실행되고 있었다.

shutil.py 파일을 리버스쉘 코드로 바꾸면 1분 마다 리버스쉘이 실행된다.

morpheus 홈디렉토리에 있는 플래그를 제출하면 미션 클리어!
사실 Dreaming 문제는 morpheus 플래그를 제출하는게 끝이다.
root 권한상승까지 안가고 끝내려니 찝찝해서 root까지 권한상승을 해보기로 했다.

morpheos에게는 sudo를 통해 모든 계정 권한으로, 패스워드 없이, 모든 명령을 실행할 수 있었고 bash 명령을 실행시켜 아주 손쉽게 root 권한까지 얻을 수 있었다.
물론 플래그 파일은 없다. 끝!!