지난 프로젝트 일지에서의 계획
1. Framework의 update 기능 및 info 명령시 각 명령어에 대한 설명 추가.
2. 선택한 인터페이스의 IP대역에 있는 호스트 스캐닝 구현

Framework의 update 기능 구현은 스캐너 툴을 완성시킨 후, github페이지에 올린 후에 구현할 것이다.!
info 명령시 각 명령어에 대한 설명 추가! scanner 툴에 대한 설명을 추가하였다.
그리고 선택한 인터페이스의 IP대역에 있는 호스트 스캔을 구현하였다.

info 명령 추가이다.

각 명령어의 사용법을 추가하였다.


(명령어 사용법 추가)


그리고 각 도구에 대한 설명을 추가하였다.


(스캐너 툴 설명 추가)


이번에 구현한 기능은 호스트 스캔 기능으로
해당 네트워크 대역에 살아있는 호스트들을 확인하는 기능이다.
나는 패킷을 직접 만들어 구현하지 않고, nmap의 기능을 이용했다.
python3의 nmap 모듈을 설치하여 사용했다.

nmap 모듈에 대한 설명이 많지 않았다...
그렇기에 python3 nmap 파일을 직접 열어서 확인해봐야했다.

nmap에는 PortScanner 클래스가 있고 이 클래스를 생성해 nmap 스캔 작업을 한다.

PortScanner 뿐 아니라 다른 기능의 클래스들이 있다. 비동기화스캐너라고 콜백함수를 이용하는 스캐너가 있었는데, 이거 잘쓰면 재밌는 기능을 구현할 수 있을 듯했다.



(포트스캐너 생성)


생성한 nmap 클래스 nm을 scan_menu에 전달하여 구현하였다.



(호스트 스캔)


호스트 스캔을 하고 아래에는 각 정보를 Tool 클래스의 hosts 사전형 변수에 저장한다.
기능을 구현하고 보니 지저분하다는 생각이 들었다.
scan_menu 함수 안에 두기 지저분하니 이걸 함수로 옮겨야겠다.(다음에 옮길 것이다.)

그리고 가지고 있는 hosts 정보를 출력할 함수도 scan_func에 구현하였다.
scanner 툴에 스캔한 호스트 정보를 출력해주는 기능이 있는데, 그 때 이 함수를 호출할 것이다.

(정보는 더 많게!)


(정보 출력 함수)


실행 모습이다.


(스캔 중인 모습)


스캔 결과 아주 흡족하게 나왔다.



(스캔결과)


이 정보 만 가지고는 부족하다.
이 호스트 정보를 가지고 디테일한 스캐닝을 하는 기능을 구현 할 것이다.


오늘 구현한 기능
- scanner의 host scan 기능
- Framwork의 info 명령 추가

현재까지 구현한 기능
1. Framework 내 tool import 등록, 연동
2. 각 Framework의 기능 구현(list, use, info, exit)
3. scanner의 인터페이스 설정 부분, 설정된 인터페이스의 IP정보 구하기.
4. scanner의 host scan 기능

다음 계획
1. 호스트 리스트를 가지고 디테일 스캐닝 기능 구현

2. Framework의 update 기능 구현


지난 프로젝트 일지에서의 계획이다.
1. Framework의 update 기능 및 info 명령시 각 명령어에 대한 설명 추가.
2. Scanner 제작.

아직 1번은 못했다. (하기는 쉽지만, 귀찮아서... 하지만 조만간 곧 1번은 마무리할것이다.!! 미루는 것은 안좋으므로!)
오늘은 지난 계획 중 2번 스캐너 제작에 돌입했다.

오늘은 스캐너 제작에서 첫 단계인 현재 내 컴퓨터에서 인터페이스를 설정하고, 해당 인터페이스의 IP주소, Gateway 주소, Netmask 정보를 구해오는 작업을 했다.

Python의 netifaces 라이브러리를 사용했다.

제일 첫 단계로 인터페이스를 잡아야한다.!


(인터페이스 리스트)


netifaces 라이브러리의 interfaces 함수를 사용하면 현재 호스트의 인터페이스 리스트가 반환된다.


(결과)


루푸백 인터페이스와 eth0 인터페이스 2개가 나온 것을 확인 할 수 있었다.

이걸 가지고 scanner 시작할 때 원하는 인터페이스를 선택하도록 할 것이다.

인터페이스가 설정 되었으면 그 인터페이스의 ip 정보를 확인해 볼 수 있다.


(ifaddresses)


ifaddresses 함수를 사용한다.



(결과)


결과 해당 인터페이스의 ip정보가 나온다. 난 IPv4를 사용할 것이다.


(AF_INET)


(AF_INET)


IPv4리스트의 첫번째 원소가 IPv4의 정보이다. 타입은 사전형이므로

'addr' 키를 넣으면 해당 ip 주소 데이터가 나온다.


(결과)


로컬 호스트의 ip 주소를 구하는 것 까지 했다.

이제 gateway 주소를 얻어올 것이다.

gateway정보는 gateways 함수를 사용하면 된다.


(gateways() 함수)


(결과)


이 중에서 default를 사용할 것이다.

그러면 기본 gateway의 주소를 구할 수 있게 된다.


(gateway주소)


(결과)


gateway의 주소를 구했다.

이제 인터페이스가 연결되어있는 네트워크의 사이즈를 파악하기 위해 netmask를 구해야한다.

netmask 정보는 아까 ifaddresses 함수의 결과 안에 있었다. 거기서 netmask 정보를 가져온다.


(netmask)


(결과)


netmask 정보까지 구했다.

이제 netmask의 정보를 가지고 cidr도 구할 수 있으며, 이를 이용해 네트워크 호스트 스캔을 할 때 이용할 수 있을 것이다.

지금까지 알아본 정보로 CoffeeNet 프로젝트에 기능을 추가했다.


(CoffeeNet 의 Tool, Scanner)


함수는 파일을 분리하여

scan_func 파일을 만들어 스캐너에 필요한 함수를 전부 여기다 정리해 둘 것이다.


(scan_func.py)



그러면 지금까지 정리된 툴을 실행시켜보겠다.



(use scanner)


scanner를 사용한다고 말한다.

그러면 인터페이스를 설정하는 선택지가 나온다.



(번호 선택)


여기서 번호만 적어도 되고, use [숫자] 이렇게 적어도 가능하도록 만들었다.

인터페이스를 잡게되면

처음 Scanner시작화면이 뜨게된다.



(Scanner 화면)


스캐너 화면에는 해당 선택된 네트워크 인터페이스 정보를 보여준다.
그리고 현재 로컬 IP주소와 Gateway 주소를 보여주게 만들었다.

오늘 구현한 기능
- scanner의 인터페이스 설정 부분, 설정된 인터페이스의 IP정보 구하기.

현재까지 구현한 기능
1. Framework 내 tool import 등록, 연동
2. 각 Framework의 기능 구현(list, use, info, exit)
3. scanner의 인터페이스 설정 부분, 설정된 인터페이스의 IP정보 구하기.

다음 계획
1. Framework의 update 기능 및 info 명령시 각 명령어에 대한 설명 추가.

2. 선택한 인터페이스의 IP대역에 있는 호스트 스캐닝 구현

 최근 Veil Framework 소스코드를 분석해보다가, 나도 만들 수 있겠는데? 라는 자신감이 들었다.
그리하여 내가 직접 tool을 개발해보기로 했다.

아주 그럴싸하게 여러개의 tool을 통합해서 사용할 수 있는 Framework를 간단하게 만들 것이다.
그리고 그 안에 tool을 개발하여 통합시킬 계획이다.

프로젝트 이름은 CoffeeNet이다.
나는 소규모 Network를 공격하기 쉽게 이러한 프로그램을 만들기로 결심했다.
CoffeeNet 프레임워크 안에 앞으로 개발할 도구는 2가지이다.
1. Scanner
2. Sniffer

첫번째 도구는 스캐너이다. 해당 네트워크에 살아있는 호스트를 탐색하고, 열려있는 포트 등을 체크하기 위한 도구로 사용할 것이다.
두번째로는 sniffer 환경을 구축하는 도구를 만들 것이다. ARP Spoofing, Evil twin 등 해당 네트워크안의 패킷들을 스니핑 할 수 있는 환경을 구축하는 도구로 사용할 것이다.

그리고 CoffeeNet 프레임 워크에서 1번도구와 2번 도구를 자동화시켜서 알아서 결과까지 내보이는 기능을 탑재시키는 것이 최종 목표이다.

먼저 프레임 워크 틀을 만들었다.



(main menu)


위 화면은 메인메뉴이다.
실제 Framework처럼 만들고 싶었다. (만들다보니 퀄리티에 욕심이 자꾸 났다.)

CoffeeNet은 사용자와 직접 대화하듯 interactive하게 만들었다.

list 기능 구현이다.


(list)


list 명령을 입력하면 현재 import된 툴 이름을 출력해준다.


use 명령


(use)


use 명령 뒤에는 해당 툴 번호 혹은 툴 이름을 입력해주면 된다.
그렇게 되면 해당 툴이 실행된다.

하지만 아직 안에 내용물은 없는 껍데기만 만들어놨다.

info 명령


(info)


info 명령시 help와 같은 기능을 한다.
인자 없이 info 명령을 하면 이 툴에대한 간단한 소개를 해주고, 각 명령어의 사용법을 알려준다.
(아직 모든 명령어의 설명을 등록하지 못했다.)

info 명령어 뒤에 툴 이름을 적으면


info [tool_name]



해당 툴의 간단한 설명이 나온다.

exit 명령어


(exit)


exit 명령을 하게 되면 프로그램을 종료시켜준다.

update 기능은 앞으로 추가할 내용이다.

큰 구조는 아래와 같다.


(CoffeeNet 구조)




현재 구현한 기능
1. Framework 내 tool import 등록, 연동
2. 각 Framework의 기능 구현(list, use, info, exit)

다음 계획
1. Framework의 update 기능 및 info 명령시 각 명령어에 대한 설명 추가.

2. Scanner 제작.


이 문제도 하루 정도 걸려서 풀게 되었다..

취약점을 쉽게 발견했지만 RSA 관련해 이해해야할 부분이 있었고
그 부분에 있어서 시간이 좀 걸렸다.

RSA 암호기법은 정보보안기사 공부할 때도 했고, 암호 공부할 때도 했었는데
실제로 암호화 과정, 복호화 과정을 대강 이렇다~ 하고 넘어갔기 때문에
문제를 풀 때 다시 공부를 해야했다.

이 문제를 풀면서 암호화, 복호화 과정을 철저하게 되집은 점이 가장 기분 좋았다.

먼저, 문제 프로그램의 보호기법을 확인하였다.


(보호기법)


오랜만에 pwnable.kr 의 문제를 풀어서 그런지 조금 새로웠다.
이유는 다른 대회문제나 다른 사이트의 포너블같은 경우
아주 당연하게 PIE, NX 기본적인 보호기법이 설정 되어있는데
pwnable.kr의 루키 문제라서 그런지 보호기법이 거의 없었다. 여기서는 카나리 하나 적용되었다.
(그리고 바이너리파일에 C코드 심볼이 첨부되어있어서 읽기도 편했다.)

바이너리 파일을 직접 핸드레이를 하고 전체적인 과정을 파악해 보았다.
크게 첫번째로 RSA 암호화의 p,q e, d를 설정하는 과정이 있다.

그리고 암호화를 위해 선택지2번을 입력하면 아래와 같이 나온다.


(암호화)


읽을 데이터 길이를 입력받는다.
그리고 데이터를 입력받는다.

여기서 조금 특이한 과정이 있었는데, 그 이유는 모르겠다.
의도적으로 이상하게 만든건지 이유가 있어서 그런건지.
특이했던건
입력을 fread로 받는데, 1바이트씩 받는다.

그래서 스택의 어느 변수에 하나씩 받아 온 후, 한 자씩 어느 글로벌 배열변수에 저장한다.



(평문 버퍼)


바로 이곳에 한자씩 저장을 한다.

그리고 이 버퍼에서 한 자씩 다시 가져와서 암호화 과정을 걸 친 후 글로벌 배열변수(암호화버퍼)에 저장한다.



(암호문 버퍼)


위 암호문 버퍼에 저장하게 된다.

그리고 이 버퍼에 있는 것을 다시 한자씩 출력용으로 데이터를 가져와서 스택에 저장한 후
그 스택에 있는 것을 출력한다.

위의 과정을 걸쳐 암호화가 진행된다.
여기서 취약한 점은 바로 버퍼 크기이다.
평문 버퍼와 암호문 버퍼 같은 사이즈로 설정해놓았다. 그리고는 평문에서 한자씩 가져와서 RSA 과정을 걸쳐서 암호문 버퍼에 저장한다.
그런데 평문버퍼의 1바이트를 가져와서 RSA 암호화를 거치면 4바이트가 되는데 이 것을 암호문 버퍼에 저장을 하기 때문에 암호문 버퍼는 당연히 넘치게 된다.

(overflow)


바로 아래에 있는 func 배열 변수까지 넘쳐버렸다.

암호문 버퍼에 있는 것을 스택으로 옮기는 과정에서도 마찬가지다. 그렇기에 스택도 overflow 낼 수 있다. 하지만 스택 카나리가 있기 때문에 메모리 leak할 방법을 찾아내어야한다.
그렇기에 나는 암호문 버퍼를 overflow하여 func 배열변수를 덮어쓸 생각을 했다.
func 배열 변수에는 함수 주소들이 있다. func[0] 에는 set_key 함수 주소가 들어있었는데,
이 주소를 덮어쓴 후, set key를 실행시키면 우리가 덮어쓴 주소가 시작되게 할 것이다.

나는 평문버퍼 앞부분에 쉘코드를 넣고, func[0] 에다가 평문버퍼의 주소를 덮어쓸 생각을 했다.
왜 자신있게 했느냐 하면, NX가 설정되어있지 않기 때문에!! 이럴때 쉘코드를 마구마구 맘껏 써야겠다는 생각이 들었다.

그렇다면 이제 문제는 어떻게하여 평문버퍼의 주소 0x602560으로 설정할 것인가 였다.
(*평문버퍼의 주소를 확정할 수 있는 이유는 바로 PIE가 설정되어있지 않기 때문에 고정적이기 때문이다.)
한 글자, 즉 0~255의 숫자를 RSA하여 0x602560으로 만들어야한다.
여기까지는 금방 파악했으나 평문버퍼의 주소를 만들기 위해 시간이 꾀 걸렸다.

RSA 암호화 과정
pow(m,e) % N = c
m은 평문이고 c는 암호문이다.

우리는 c가 0x602560이기만 하면 된다.

이것저것 시도하다가 생각이 트인 때가 있었다.


(p,q 설정과정)


p,q,d,e를 설정하는 부분인데
실제 RSA 에서는 P와 Q는 굉장히 큰 소수여야한다.
소수!!

하지만 이 프로그램에서는 소수인지 검사하지 않는다. (아마 그렇게 되면 프로그램이 무거워져서 그랬나?)
무튼, 여기 프로그램에서는 p와 q가 소수가 아니어도 된다는 점!
그렇기에 값을 마구마구 설정해서 원하는 c(암호문)값만 만들 수 있으면 된다는 거였다.

pow(m, e) % N = 0x602560
10진수로 쓰면
pow(m, e) % N = 6301024 이고
다시 정리하면
N * a + 6301024 = pow(m, e)
여기서 m은 14, e는 7이라고 설정했다.
그래야 6301024의 근처 숫자가 되기 때문이다.
그렇게 되면 
N * a = 99112480
이 되고, N은 6301024 보다 큰 조건을 만족하고, 작은 N의 값을 12389060 으로 설정했다.

그러면 p,q 값을 찾아야하는데
이 값은 잘 정해야한다. 이유는 p,q값이 char로 설정되어있기 때문에 0x0000~부터 0x7FFF 까지의 숫자여야한다.(64비트이기 때문)


N이 12389060 이기 때문에 p,q를 1217, 10180으로 설정했다.

그리고 우리가 원하는 평문버퍼 주소(0x602560)으로 나오는지 확인해보았다.



(확인)



(확인 결과)


확인해보니
딱 나왔다!

이제 끝났다!

func[0]부분까지만 덮어쓰려면 265만큼 넣으면 된다.



(265 바이트 입력)



(결과모습)


결과 딱 func[0] 까지만 덮어쓰여진 것을 볼 수 있다.

이제
페이로드 앞부분에는 쉘코드를 올리고
나머지 241바이트
그 후 0x0e  값을 보낼 것이다.
마지막 바이트가 RSA 과정을 거쳐 평문버퍼의 주소가 된다.

exploit을 작성했다.



(쉘코드 및 변수 설정)



(exploit)



(exploit 결과)


문제 풀이 끝

'WarGame > 500 Project' 카테고리의 다른 글

(67/500) pwnable.kr - note  (0) 2018.03.29
(65/500) pwnable.kr - echo2  (0) 2018.03.08
(64/500) backdoor - Enter the matrix  (0) 2017.10.31
(63/500) pwnable.kr - syscall  (2) 2017.10.22
(62/500) NOE.systems - BURYBURY  (0) 2017.10.01
오랜만에 포너블 문제를 풀었다.
그간 다른 것들을 공부하느라 워게임을 못풀었는데

감떨어질까봐 하나 풀어봤는데, 역시 감이 떨어진 느낌이 들었다.
틈틈히 다시 풀어봐야겠다.!

pwnable.kr 사이트의 echo2 문제이다.

이 문제는 echo 서비스를 실행하는 프로그램을 연결해두었다.


(echo2 문제)


파일을 주기 때문에 직접 다운받아서 분석해볼 수 있다.



(보호기법)


보호기법을 보면 되있는게 없다.
그래서 50점 짜리였나보다. (오래걸리진 않았지만 그닥 쉽게 풀진 않은것으로 보아 감이 많이 떨어진 느낌이다.)

echo2 프로그램에서는 2가지 서비스를 제공한다.
FSB 라는 항목인데 여기서는 스택에 입력을 받았다가, 입력한 문자열을 그대로 다시 화면에 보여주는 기능
UAF 라는 항목에서는 힙영역에 입력을 받아 저장했다가 다시 화면에 보여주는 기능을 한다.

대놓고 어떤 취약점이 있는지 알려주었다.

그러면 FSB 취약점부터 확인해본다!



(fsb 취약점)


printf 에다가 인자를 문자열 버퍼 주소를 그대로 넣는 것을 볼 수 있다.
이렇게 되면 fsb 취약점이 생기게 된다.

그런데 실제로 문제를 풀 때 여기서 조금 헤메었다.
그 이유는
실제 내 컴퓨터에서는 메모리 노출 정보가 달랐다.

아래 사진을 보면서 설명하겠다.



(내 컴퓨터)


내 컴퓨터에서는
64비트 컴퓨터의 함수 전달 방식처럼 레지스터의 값들을 출력해주었다.
rdi, rsi rdx, rcx, r8, r9 이 순서대로 출력이 된다.
실제 위 사진처럼 해당 레지스터 정보가 출력된다.

그런데 실제 서버에 연결을 하였을 때는 달랐다.



(실제 연결시)


실제 연결시 레지스터의 정보들이 나오는 것이 아니라 바로 스택의 정보가 나왔다.
위 사진을 보면 문자열이 나오고 있는 것을 확인 할 수 있다.
이유는 잘 모르겠지만...
이 것 때문에 처음에 만든 exploit을 뜯어고쳤다.
처음에 만든 exploit은 내 컴퓨터에서는 돌아갔는데 서버에서는 동작하지 않았다.
그 이유가 stack 주소를 잘 찾지 못했기 때문이었다.

실제로 연결하였을 때 문자열이 나오는 것을 보고 다시 메모리 정보를 leak하였다.



(눈에 띄는 주소)


해보았을 때 문자열이 다 끝나고 다음의 값들이 나오는 것을 볼 수 있었는데

400acb라는 값이 눈에 띄었다.
나는 이 값이 무엇인지 알고 있었다. 이 주소는 main 함수의 주소였다.

그렇다! ret 주소였다.



(ret 주소)


그렇다면?

그 앞에 있는 것은 rbp 주소! 스택주소를 알아내었다.



(rbp 주소)


그 다음 UAF 취약점이다.
나는 그 동안 heap 영역에 대해서 정리를 해왔는데
이 부분에 있어서 너무 쉽게 응용할 수 있게 되었다.

main에서 함수가 호출 된 후 마지막에 cleanup함수가 호출된다.



(cleanup)


cleanup 함수에서는 o객체를 free해준다.
o객체 안에는 greeting함수와 byebye함수 주소가 있고
실제로 greeting과 byebye함수를 호출할 때 o객체에 접근하여 호출한다.

여기서 4를 입력으로 주어 종료를 시도하면 cleanup 함수가 실행된다.
문제는 그 다음에 다시 정말 종료할 지 묻는다.
이 때 n을 눌러 종료하지 않게 되면
다시 main의 루프로 돌아가게 되는데 이 때는 이미 o객체가 free 된 후에서 프로그램이 진행된다.

echo2함수 안에서 보면



(greeting 함수 호출)


greeting 함수 호출 할 때 o객체에 접근하여



(greeting 함수)


greeting함수를 호출하는 것을 볼 수 있다.


그렇다는 건 이 값을 덮어쓸 수 있다는것! 왜냐!
바로 UAF (3번)을 입력하면 malloc을 다시 주는데 이 주소를 다시 할당해주기 때문이다.


(재할당)


실제로 다시 해보면 malloc을 통해서 받는 주소는 아까 free된 o객체의 주소를 넘겨주었다.
이제 이 주소에다가 0x20 만큼 적을 수 있으므로 greeting 함수위치에 다른 주소를 적을 수 있다는 것이다.

우리는 쉘코드를 스택에 올리고 그 쉘코드 주소를 여기다 적어줄 것이다.

쉘코드를 스택에 올릴 것인데, 바로 맨 처음 이름을 입력받는 부분이다.

입력을 받는 부분은 아래와 같다.



(이름 영역)


이 곳에 쉘코드를 올릴 것이고 그 위에 값을 보면 아까 우리가 확인한 rbp 값을 확인할 수 있다.
0x20 차이가 난다.

exploit 코드를 보면서 정리하겠다.



(shellcode 업로드)


쉘코드를 업로드하는 부분이다. 이름입력할 때 쉘코드를 업로드하고
이 부분은 스택영역이다.

그 후 스택 주소를 찾아서 정확한 쉘코드 주소를 찾아야한다.



(쉘코드 주소 찾기)


그 후 UAF 취약점 공략이다.



(UAF 공략)


UAF를 통해 o객체에 다시 접근하여 greeting 주소 부분에다가 shellcode 주소를 적어준다.

그리고 아무 기능이나 다시 시작하면 greeting 함수가 시작 될때 shellcode가 실행되어 쉘을 얻을 수 있다.



(쉘 획득)


문제 풀이 끝!

'WarGame > 500 Project' 카테고리의 다른 글

(67/500) pwnable.kr - note  (0) 2018.03.29
(66/500) pwnable.kr - rsa_calculator  (1) 2018.03.10
(64/500) backdoor - Enter the matrix  (0) 2017.10.31
(63/500) pwnable.kr - syscall  (2) 2017.10.22
(62/500) NOE.systems - BURYBURY  (0) 2017.10.01
이번 학기 수업에 지능시스템개론을 듣게 되어
tensorflow 관련 및 인공지능 학습에 관련된 글을 앞으로 포스팅 하게 될 것이다. :)

tensorflow는 python에서 돌아간다.
tensor = Data 로, tensorflow란 데이터의 흐름이라는 의미의 이름이다.

그럼 먼저 tensorflow를 설치해볼 것이다.
내 환경은 Centos7, 64Bit에서 설치하였다.

tensorflow를 설치하기 전, virtualenv라는 가상환경을 만들어 줄 것이다.
tensorflow 공식 홈페이지에서도 이를 추천한다.
이유는 tensorflow와 의존성이 있는 모듈들을 같이 설치해야하는데, 이 때 기존에 있던 파이썬 모듈들을 덮어쓰게 되는 경우가 있다는 것이다.

tensorflow 설치 전 과정이다.


(설치전 과정)


설치 전 pip과 개발도구, virtualenv를 설치해준다.

그 후 각각 업그레이드를 해준다. 안했을 경우 버전이 안맞아 에러가 날 수 있기 때문에!



(pip upgrade)



(설치도구 upgrade)



(virtualenv 업그레이드)


여기 까지 준비 설치 끝이다.

가상환경을 설치했으니 가상환경을 활성화 시켜줄 것이다.

그 전에 tensorflow를 위한 가상환경을 만들어준다.



(가상환경 만들기)



(활성화)


그 후 위 처럼 활성화 시켜준다. 

활성화 해제할 때에는
#deactivate
라는 명령으로 다시 빠져나올 수 있다.

그 후 tensorflow를 설치해준다.



(tensorflow 설치)


여기까지가 설치 끝이다!

그러면 간단하게 tensorflow 설치를 확인하는 겸 간단하게 사용해 볼 것이다.

제일 먼저하는 Hello 문장을 찍어보는 코드이다.



(Hello 코드)


tensorflow는 이름이 기니 tf로 import한다.
그리고 tf.constant 로 하나의 노드를 생성한다. 여기서는 hello에 어떤 문자열이 담긴 노드를 하나 만든 것이다.

그리고 바로 실행하는 것이 아니라
session을 만들어줘야한다. 세션을 만들어 준 후
그 세션에 노드를 넣어주면서 run(실행)을 해주면 노드의 결과가 나온다.

여기서는 간단히 문자열이 담긴 노드를 넣었으니 문자열이 그대로 나오게 된다.


(실행 결과)


실행 결과 Hello, TensorFlow! 라는 문자열이 나왔다.
앞에 b 라고 적혀있는 것을 봐서 찍힌 문자가 python의 문자열이 아니라 바이트 형태로 노드에 저장이 되고 출력된 것임을 알 수 있다.
( 그 위에 경고문이 뜨는데 무시해도 된다. )

그 다음으로 간단한 덧셈을 하는 것을 살펴볼 것이다.


(덧셈)


덧셈할 수를 가진 노드를 두개 만들어준다.
그 후 덧셈을 할 노드를 만들어주는데 여기서 node1과 node2를 넣어주면서 node3을 만들었다.

그리고 세션을 만들어준 후

세션에 덧셈노드인 node3을 넣어주면서 실행하면 덧셈의 결과가 나오게 된다.


(덧셈 결과)


그러면 이렇게 정해진 숫자가 아닌 변수처럼 어떤 값이 들어와도 계산이 되는
노드 (시스템)을 만들려면?

placeholder를 이용하면 된다.


(placeholder)


placeholder는 마치 변수처럼 빈 통이다. 
a, b에는 float 32비트 타입의 숫자를 담을 수 있는 통이다.
그리고 그 아래 adder_node 는 덧셈을 해주는 노드인데, a통과 b통에 담긴 숫자를 더해주는 노드이다.

adder_node를 실행시키기전에 해야할 일은?
-> a통과 b통에 채워주는 것이다.

feed_dict를 통해 a,b 변수에 값을 채워준다.



(실행 결과)


실행 시 위의 결과처럼 
덧셈이 된다.
두번째의 코드처럼 배열이 들어와도 괜찮다.

(* 참조 - 모두를 위한 딥러닝 시즌1, Sung Kim)

overlapping chunk 두번째 자료이다.

이 자료 또한 어떠한 문서를 참조했다고 나와있었다.



(참조 문서)


그렇기에 나 또한 이 참조 문서 먼저 공부하였다.
이 문서에는 Heap overlapping에 관해서 3가지 정도의 방법을 제시했다.
Heap Overlapping에 관해 조금씩 다른 방법들을 제시했는데,
다들 비슷비슷한 느낌이었다. 그래도 많은 방법들을 봐두는 것이 좋은 것 같다.

지금까지 Heap Overlapping에 관련된 공격들을 정리하면서 깨달은 점이 있다.
그건
전부 Chunk 구조체의 SIZE 필드를 조작하므로써 공격을 한다는 것이었다.
PREV_SIZE라 던가, SIZE 필드라던가 chunk의 사이즈를 Allocator에게 솎여서 잘못된 할당과 해제를 유발하므로써 기존에 있던 chunk에 중첩되는 chunk를 만들게 하는 방식이었다.

그렇기에 최신 glibc에서는 PREV_SIZE 체크 라던가 SIZE 체크 부분이 추가되었고
이 부분을 우회하는 것을 최근 how2heap에서 본 기억이 난다.

무튼,

heap overlapping 2번째 분석을 시작하자!



(5개의 힙)


5개의 힙 영역을 할당한다.
malloc 사이즈는 1000으로 16진수로는 3E8이다.

뒷자리가 8인 것을 보면 눈치 채겠지만, off-by-one에 취약할 것이다.



(각 힙영역의 범위)


각 힙영역을 표시하기 위해 해당 영역에 문자들을 넣어주었다.



(문자 입력)


그 후 p4 영역을 free해준다.
왜 굳이 p4일까?
-> p4를 free해주면 top청크와 병합되지 않는다. 이유는 top청크와 인접한 p5가 존재하기 때문에 병합되지 않는다. 그리고 공격의 개략적인 시나리오를 말하자면 p1 청크에서 off-by-one으로 p2의 청크 사이즈를 조작할 것이고, p3의 영역을 포함하고 있는 중첩된 heap영역을 할당받는 것이 우리의 목적이다.

: 그렇기에 p4를 free해준 것이다.



(free)


p4를 free해준 후

p1을 이용해 p2의 청크 사이즈를 조작한다.



(청크 사이즈 조작)


조작한 사이즈는
p2 청크의 사이즈 + p3 청크의 사이즈 + PREV_IN_USE + sizeof(size_t)
조작할 사이즈는 p2와 p3를 합한 청크 사이즈로 만들 것이다.
뒤에 prev_in_use 값은 0x1 로써, 이전 청크가 사용중이라는 것을 알려주기 위해 설정한다. 그렇지 않으면 이전 청크와의 병합도 시도할 것이다.
sizeof 값이 더해지는 이유는 p2, p3의 헤더 크기를 추가해주는 것이다. 




(p2 free)


그 후 p2를 free해준다.
그렇게 되면 다음과 같은 일이 일어난다.
1. allocator는 p2를 free하려고 한다.
2. p2의 PREV_IN_USE 비트를 보니 이전 청크를 사용하는 군!
3. next chunk를 검사한다. (p2 주소 + p2의 사이즈 = p4) = next chunk는 p4
=> 왜 다음 청크가 p3이 아닌지? 방금 우리는 p2의 사이즈를 p2,p3을 합한 덩어리로 조작했다. 그렇기에 p2 주소 + (조작된)p2의 사이즈 = p4의 주소 이고 p4가 next chunk가 된다.
4. p4(next chunk)를 확인해보니, free 되어있네???
5. p4랑 병합해야지~, p2는 결론적으로 p2,p3,p4를 포함한 커다란 free chunk가 되버린다.

그 후 malloc을 통해 p3을 포함할만큼 커다란 영역을 할당받는다면?



(큰 영역 할당)


그렇게 되면 p3영역이 포함된 영역을 할당해준다.



(할당된 영역)


할당 된 영역을 보면 p6 사이에 p3영역이 들어있는 것을 확인 할 수 있다.

현재 p3에 들어있는 데이터



(p3의 데이터)


그 후 p6을 F로 1500 글자 채워준다.



(F로 채우기)


그렇게 되면 500자가 p3영역을 침범하게 되어 p3 데이터 앞부분이 F로 채워지게 된다.



(확인)


확인을 통해 p3가 중첩된 영역을 할당받은 것을 확인 할 수 있다.

'Vulnerability_Tech > About Heap' 카테고리의 다른 글

(how2heap) - overlapping_chunks  (0) 2018.02.22
(how2heap) - house of lore  (0) 2018.02.21
(how2heap) - poison null byte  (0) 2018.02.20
malloc의 사용가능 영역(HEAP)  (0) 2018.02.20
(how2heap) - house_of_spirit  (0) 2018.01.17
저번 까지는 화면에 우주선을 움직이게 까지 했다.
이번에는 게임의 하이라이트 충돌이다.

물체간에 부딛히는 것을 인식하게 하는 것이다.
여기에는 실제로 많은 물리 지식과 수학적 지식이 필요하다.
나도 책을 여러번 읽고 이해를 할 수 있었다.
여기에 충돌에 관한 물리적 내용을 다루지는 않을 것이다.
실제 자세한 구현과 코드의 내용을 보려면 직접 책을 참고하는 것이 좋을 것이다!
(2d 게임 프로그래밍, 찰스 켈리)

이 글의 목적은 게임 상의 우주선이 게임 화면 안에서 갇혀있는 것이다.
우주선이 왔다리 갔다리 하는데 화면 끝으로 이동했을 때 넘어가는 것이 아니라
공이 벽에 튕기듯이 튕겨서 게임 화면 안에서 계속 돌아댕기는 프로그램을 만들 것이다.

충돌을 다루기 위해 벡터를 사용해야한다.
DirectX에서 제공하는 벡터 타입이다.
벡터 연산들도 제공한다.

그러나 이름이 길고 쓰기 불편하므로 우리가 이해하기 쉽게 간단한 이름으로

캡슐화 시켰다.


(벡터 재정의)



(벡터 연산)


위와 같이 벡터 타입과, 벡터 연산들을 우리가 사용하기 편하게 함수안에 넣어두었다.

그 다음 충돌을 다루게 하기 위해 Entity 라는 이름의 클래스를 만들어 볼 것이다.
Entity는 해석하면 객체이지만, 여기서는 고유명사처럼 Entity라고 말할 것이다.

Entity는 우리가 충돌을 인식하게 하기 위해 우리가 원하는 객체들을 Entity 클래스로 선언할 것이다.

그리고 각종 충돌에 관한 함수들을 집어 넣을 것이다.



(entity 이름 영역)


Entity의 이름 영역에는 충돌타입과 중력상수가 적혀있다.
충돌 타입은
원형 충돌
사각형 충돌
회전 사각형 충돌
복합 충돌
으로 구분할 수 있다.

그러한 것들을 이름영역에 정의해두었다.

다음은 Entity 클래스의 속성 변수들을 볼 것이다.



(속성 변수)


가지고 있는 속성 변수에는 위와 같다.

그리고 protected 영역에 선언된 함수들이다.



(protected 함수)


위 함수들은 다른 클래스 밖에서 호출되지 않고 내부에서만 호출되게 하기 위해 protected 안에서 선언되어있다.

이제 public 영역의 함수들을 볼 것이다.



(생성자)


생성자이다.
초기값들을 설정하는 부분이다.

다음으로는 초기화 함수이다.



(초기화 함수)


초기화 함수가 조금 특이하다.
첫번째 인자가 게임포인터라는 것만 빼면 Image의 초기화 함수랑 똑같다.
심지어 내부에서 Image의 초기화 함수를 호출한다.

게임 포인터에서 입력을 input으로 저장해둔다.
게임 중 입력 받아놓은 지역변수 값들을 가져와서 저장해두는 역할을 한다.

(activate() & update())


activate() 함수는 Entity 객체가 충돌관련 연산이 일어나게 할지 활성화 여부를 활성화 시키는 것이다.

update 함수는 계산된 속도 변화를 적용시킨다.
이전에 충돌과 관련된 계산이 끝난 후 변화 속도를 deltaV에 저장해 두고 여기서 적용시킨다.

그 후 Image의 update를 호출한다.


(ai 함수)


AI 함수는 인공지능에 관련된 함수이다.
지금 현재 우리의 객체에는 인공지능이 들어가지 않는다.
그렇지만 Entity 클래스의 객체를 인스턴스화 할 수 있게 하기 위해 빈 함수로 제공해둔다.

이렇게 하면 Entity 클래스에서 객체를 직접 생성할 수 있다.



(충돌 함수)


충돌에 관련된 함수이다.
위 함수는 각 객체의 충돌 타입을 확인 한 후 각 객체의 충돌 타입에 맞게 다시 충돌탐지 함수들을 호출한다.
각 충돌 탐지에 관한 함수들, 내용들은
시간이 된다면 따로 정리해서 올릴 예정이다.

그 후 이 전까지와 달라진 점이 있다.
이젠 Entity를 사용해 각 객체를 호출 할 것이다.
그러기 전에 행성과 우주선이라는 클래스를 따로 만들 것이다.
그리고 이 우주선과 행성 클래스는 Entity 클래스를 상속한다.
우주선과 행성 클래스를 따로 만드는 이유는 각 객체의 값들을 따로 관리하는게 좋기 때문이다.
여기저기 섞여 있는 것보다는!

planet 부터 보자!


(planet 헤더)


헤더 파일에 planet 의 속성들이 들어있다.



(planet 생성자)


생성자는 간단히 이름영역에서 설정한 값들을
변수에 초기값으로 설정하는 역할을 한다.

다음은 ship 클래스이다.


(ship 헤더)


planet 과 같이 속성 설정값들이 있다. ( 이 값들은 애니메이션과 관련된 설정들도 있고 지난 글들에서 다루어왔던 값들이므로 설명은 생략하겠다. )

planet과 다른점은 update 함수가 있다.

이 update 함수는 우주선이 벽에 부딛히고 튕겨져 나오는 것을 작성하기 위해 여기서 상속받아서 작성하였다.


(ship의 생성자)


planet과 마찬가지로 ship의 생성자 또한 이름영역에서 설정된 값들을 가지고 초기값을 설정하며 만들어진다.



(update 함수)


update 함수이다.
이 함수에서는 맨 먼저 Entity 의 update가 실행된다. Entity의 update에서 충돌 결과의 움직임이 적용이 되고 Image의 update가 실행이 된 후이다. 마지막으로 화면에 그려지기 전에 이루어지는 update이다.

Entity의 update에서 적용된 속도 값들을 이용하여 해당 우주선의 화면상 위치를 잡는 모습이 보이고
추가적으로 우주선이 천천히 뱅글뱅글 돌아가도록 spriteData의 angle 값을 바꾸고 있는 모습도 보인다.

그 뒤로 이제 화면에 부딛히는 과정을 체크한 후 부딛히면 날라온 방향에서 부딛힌 축에 뒤집히게 -1을 곱하여 반대로 뒤집어준다.

이제 실제 spacewar 파일에서 우주선과 행성 등을 다시 선언해주어야한다.
이 전까지는 Image로 선언했기 때문이다.



(재선언)


이 때 우주선은 Ship으로 행성은 Planet으로 선언했다.
둘 모두 Entity를 상속했고 Entity이고 Entity 는 Image를 상속했다.

실행해보면 우주선이 게임화면에서 계속 튕기면서 돌아댕기는 모습을 볼 수 있다.



(우주선 표류)



(우주선 표류)


다음에는 두 객체가 충돌하는 것에 관련해서 알아볼 것이다.
아까 Entity에서 충돌에 관련해 만들어두었지만 아직 안썼었다.

(* 참조 - 2d 게임 프로그래밍, 찰스 켈리)

  오늘 분석한 자료는 overlapping chunks이다.
이 방법은 The Forgotten Chunks 라는 문서에서 가져왔다고 하여

해당 문서를 먼저 읽어보았다.



(참고 문서)


저 페이퍼는 정말 재밌었다.
작명센스도.! 잊혀진 청크..!

전략을 간략히 말하자면
heap overflow를 통하여 청크의 SIZE 필드를 조작한다.
그리하여 malloc 혹은 free를 통하여 중첩된 영역을 얻는다.

* 중첩된 영역을 얻는다 라는 것에 대해서
-> 예를들어 A라는 malloc 영역이 주소 4~8 이라고 하자.
그리고 B에 malloc을 받는데 0~11의 주소로 할당을 받는다고 한다면?
B 주소 영역에 A의 주소 영역이 포함되어있다. 그렇기에 B에 작성하므로 A영역의 데이터를 건들일 수 도 있고, A에 작성하므로써 B영역의 데이터를 건들일 수 도 있는 상황이 된다.

우리는 중첩된 영역을 얻는 것을 목표로 할 것이다.

먼저 3개의 malloc 주소를 할당받는다.


(malloc)


이 때 보면 malloc의 사이즈가 조금 의도된 듯한 느낌이 든다.
바로 -8 을 해준 이유는 사용 가능한 영역까지 꽉 할당받기 위해서이다.

포스트 글 malloc의 사용가능 영역
에 정리해 둔것을 참고하면 좋다.
https://blog.naver.com/rlagkstn1426/221212549437

실제의 상황에 위와같이 할당을 받고 문자열을 입력받는다면
쉽게 다음 chunk의 사이즈 영역을 건들일 수 있다.

그 후 각각의 heap 영역을 표시하기 위해 아래와 같은 작업을 한다.



(영역 표시)


p1 에는 1로 가득채우고 , p2는 2로, p3은 3으로

그리고 p2를 free해준다.



(free)


p2 를 free 해준다.

그렇게 되면 가운데가 구멍이 뚫린 상황이 된다.
p1 - p2(free됨) - p3

실제 같으면 p1에 heap overflow를 이용하여 p2의 사이즈 영역을 덮어썻을 것이지만
테스트 코드이므로 직접 사이즈 영역에 값을 작성할 것이다.

(아직 우리가 해당 주소를 가지고 있으므로 p2에!)



(적어줄 사이즈)


evil_chunk_size는 우리가 가짜로 적을 사이즈이고,
evil_region_size는 우리가 추후에 malloc을 할 때 사용할 값이다.

우리는 지금 free 된 p2의 영역의 SIZE 필드에 0x181이라고 적을 것이다.
맨 마지막 비트가 1인 이유는 이전 청크가 사용중이기 때문에 1이라고 섬세하게 맞춰준다.



(SIZE 조작)


그렇게 되면 p2의 영역은 free된 상황인데,
free 된 청크의 크기가 0x180 이라고 설정되어있는 상황이다.

변화 과정을 메모리에서 살펴보면


(덮어쓰기 전)



(덮어쓴 후)


덮어쓴 후 p2의 메모리 영역에 SIZE를 보면 0x181이라고 덮어쓰여진 것을 확인 할 수 있다.

그렇기에 지금 freelist에 p2가 있을 것이고,
p2의 청크 크기는 0x180으로 설정되어있는 셈이다.

그렇다면 여기서 malloc(0x180)을 한다면?



(malloc(0x180))


그러면 p2의 영역을 주는데 0x180 만큼의 크기를 할당해줄 것이다.
그러면 p3는?
-> 잊혀진 것이다. 알 길이 없다. (The Forgotten Chunks 의 작명센스!)

return 된 주소를 확인해보자.


(return 된 주소)



(return 영역)


return 된 영역을 보면 원래 p2의 시작점인데
사이즈는 0x180만큼으로 그 안에 p3 영역이 있는 것을 볼 수 있다.

덮어쓰여진 것을 한번 테스트해보자!



(4로 덮어쓰기)


p4안에 p3이 들어있다.

그러면 p4의 영역을 4로 가득채우면?



(결과)


p3의 영역 또한 4로 덮어쓰여지게 된다.

그러면 p3을 3으로 다시 덮어쓰면?



(3으로 덮어쓰기)



(결과)


이번엔 p4의 영역 중간에 3으로 덮어쓰여지게 된다.

how2heap의 이 자료에서는 
해제된 free의 SIZE를 조작하고 malloc 하는 방법을 택했다.

반대로
malloc된 청크의 사이즈를 조작한 후, 다시 free하고
malloc 하는 방식으로 똑같은 결과를 만들 수 있다.

이 방법은 The Forgotten Chunks 문서에 나와있다.

'Vulnerability_Tech > About Heap' 카테고리의 다른 글

(how2heap) - overlapping_chunks_2  (0) 2018.02.27
(how2heap) - house of lore  (0) 2018.02.21
(how2heap) - poison null byte  (0) 2018.02.20
malloc의 사용가능 영역(HEAP)  (0) 2018.02.20
(how2heap) - house_of_spirit  (0) 2018.01.17
  오늘 분석한 how2heap 자료는 house of lore 이다.

이 공격을 할 수 있기 위해서는
가짜 청크 구조를 만들 수 있어야하고, 임의의 사이즈의 malloc과 free가 가능해야한다.

이 공격으로 스택영역의 주소를 malloc으로 받아서, return 주소를 덮어써서 임의의 코드를 실행해 볼 것이다.


(임의의 코드)


우리가 실행할 코드는 위의 jackpot코드이다. return 주소를 위 함수 주소로 덮어쓸 것이다.

먼저, 스택영역에 가짜 청크의 구조를 만들어야한다.


(가짜청크 영역)


가짜 청크 영역을 잡아둔다.

그 후 우리가 이용할 chunk를 하나 만든다.



(chunk 생성)


사이즈는 100으로 small bin 범위면 된다. 여기서는 small bin을 이용하였다.

알다시피, 정확한 chunk의 주소를 구하기 위해서는 2 WORD를 빼줘야한다.


(진짜 chunk 주소)


이유는
header가 2 WORD 이기 때문!이다.!

그 후 우리가 가짜 청크에 설정해주어야할 것이 있다.

이 모든 것은 아래의 small bin corruption check를 우회하기 위한 것이다.



(small bin corruption check)


위의 조건을 우회할 수 있도록, (걸리지 않도록)

가짜 청크에 값들을 넣어준다.



(조건 우회)



(메모리 모습)


가짜 청크의 모습을 정리해보면
스택버퍼1 - 스택버퍼2
이렇게 연결되어있다.

스택버퍼1 의 fd : victim
스택버퍼1의 bk : 스택버퍼2

스택버퍼2의 fd : 스택버퍼1

그 후 1000사이즈로 다시 malloc한다.



(malloc)


그 이유는
우리가 이제 victim 청크를 free할 것인데, top chunk와 병합되어버리지 않게 하기 위해
1000 사이즈 ( large bin ) 를 malloc 해주었다.

그리고 free!



(free)


victim 청크를 free한다.

그렇게 되면 아래와 같은 모습이 된다.



(free된 모습)


???
small bin에 들어가면 fd와 bk가 써져야 되는 거 아닌가??

=> 지금 안써진 이유는 바로 현재는 unsorted bin에 들어가있기 때문이다.
unsorted bin이란 일종의 캐시같은 역할인데, free가 되면 bin에 들어가기 전 unsorted bin에 들어가있는다. 그리고 그 다음 malloc때 같은 사이즈의 malloc이면 들어있던 청크가 반환되고 아니라면 원래 자기의 영역 bin에 들어가게 된다. 즉 한번의 재사용 기회가 주어지는 것이다.

그렇기에 우리는 지금 free 한 청크를 small bin에 넣고 싶기 때문에
이 한번의 재사용기회(?) unsorted bin에 있는 것을 빼주어야한다.

다른 large bin 영역의 사이즈로 다시 malloc 한다.



(malloc)


그리고 다시 victim 청크를 보면



(victim 청크)


fd와 bk가 작성되있는 것을 볼 수 있다.

아까 위에서
스택버퍼1 - 스택버퍼2 까지 연결했고,
스택버퍼1과 victim 청크를 연결할 것이다.
스택버퍼1의 fd는 아까 victim 청크 주소를 넣었고

이제 victim 청크의 bk주소에 스택버퍼1의 주소를 넣으면 된다.


(bk 조작)


그 후 victim 청크의 모습을 보면


(victim 청크)


bk가 스택버퍼1 의 주소로 조작된 것을 확인 할 수 있다.

이제 small bin의 사이즈 100인 bin에 우리가 만든
victim - 스택버퍼1 - 스택버퍼2
이렇게 주렁주렁 달려있을 것이다.

그러니 그대로 주르륵 꺼내보자.

사이즈는 100으로 malloc 한다.



(malloc)


malloc 하게 되면 victim 청크가 반환된다.
p3 = victim 청크

그러면 지금 스택버퍼1을 보면?


(스택버퍼1)


스택버퍼1의 fd가 바뀐것을 볼 수 있다.

이제 그다음 malloc(100)을 하면 우리의 스택버퍼1 차례이다.



(malloc)


p4에 스택버퍼1의 주소가 들어간다.



(결과)


그렇다면 이제 이걸 이용해서 ret 주소를 덮어쓸 것이다.


(ret 덮어쓰기)


원래 자료는 32비트에서 여서 그런지

직접 확인해보니 return 주소까지 72바이트 떨어진 자리였다.



(확인)


ret 주소에 jackpot 함수 주소가 들어가 있다.



(임의의 코드 실행)


결과적으로 마지막에 jackpot 함수(임의의 코드)가 실행되었다.

'Vulnerability_Tech > About Heap' 카테고리의 다른 글

(how2heap) - overlapping_chunks_2  (0) 2018.02.27
(how2heap) - overlapping_chunks  (0) 2018.02.22
(how2heap) - poison null byte  (0) 2018.02.20
malloc의 사용가능 영역(HEAP)  (0) 2018.02.20
(how2heap) - house_of_spirit  (0) 2018.01.17

+ Recent posts