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

취약점을 쉽게 발견했지만 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


BOF 원정대의 giant를 잡아보자!

* 소스코드


(giant.c)


중간에 복잡해보이는 코드가 있는데 친절하게 주석으로 설명이 되어있다.
ret주소에 execve 함수 주소가 들어가있는지 체크하기 위한 부분이다. 그렇기에 ret 주소에 execve를 넣어 execve 함수를 사용해서 문제를 해결하라는 의도같다.

그렇다면 execve를 어떻게 써야하나 구성인자를 살펴보자!



(execve 함수)



execve 함수의 인자
1. 파일이름(실행할파일)
2. 인자 주소(파일이름 문자열의 주소)
3. Null 문자열

예를 들기위해 C언어로 표현하여 실행해 보겠다



(execve c 코드)



(실행화면)



위와 같이 인자를 넘겨주니 execve 함수가 잘 실행되었다.

자 이제 이대로 인자를 만들어주어야한다.



(ret 주소)



ret 주소는 0x400a9d48 (execve 함수 주소) 를 넣었다.
이제 이 뒤에는 더미(4바이트) 를 넣은 후 그 뒤부터 인자가 들어가게된다.

execve 함수로 실행할 파일은 /bin/sh이다. 이 문자열을 직접 써주어도 되지만 그렇게 되면 문자열 주소를 넘겨주는 두번째 인자를 구성할 때 어려워진다. 왜냐하면 두번째 인자는 포인터 배열이기 때문에 주소와 널문자(4바이트)로 이루어져야하기 때문이다.

그렇기에 이미 로딩된 라이브러리에서 /bin/sh 문자열을 찾아 사용할 것이다.
system 함수 근처에 /bin/sh 문자열이 존재한다는 것을 알고 있기에 간단히 c코드를 만들어 /bin/sh 문자열 위치를 찾아낼 것이다.




(/bin/sh 주소찾기)



(/bin/sh 주소)



직접 gdb를 열어 해당 주소를 검색해보자.



(/bin/sh)



해당 주소에 /bin/sh 문자열이 들어있는 것을 확인 했다.

이제 첫번째 인자로 우리가 찾은 주소를 넣을 것이다.
두번째 인자를 구성해야한다. 일단 지금까지 만든 것을 토대로 메모리를 확인해보자.

A*44 + execve함수주소 + A*4(더미) + "/bin/sh주소" + "/bin/sh주소가 적힌 포인터배열 주소" + NULL

포인터배열을 만들계획이다. 일단 포인터배열 주소를 C*4 로 대체하고 메모리를 확인해보자!



(gdb 실행)


두번째 인자를 만들어줄 위치를 살펴보니 적당한 위치가 나왔다.



(4바이트 NULL)


포인터배열이기 때문에 주소뒤에 Null(4바이트)가 나와야하는데 우리가 입력은 못한다. 그렇기에 이미 존재하는걸 이용해야하는데 0xbffffae8 에 널 4바이트가 있다. 그러므로 그 앞에 문자열 주소를 입력한 후 해당 위치를 두번째 인자로 넘겨줄 것이다.

세번째 인자 Null은 0xbffffae8을 넘겨줄 것이다. 해당 주소가 이미 Null이기 때문에



(포인터배열)



위와 같이 포인터 주소배열을 만들 수 있다.



(포인터 배열)


0xbffffad4 위치에 정확히 포인터 배열이 들어갔다.
이제 두번째 인자로는 0xbffffad4를 
세번째 인자로는 0xbffffad8을 넣어줄 것이다.

마지막으로 확인 해보자.



(최종 확인)



해당 인자에 알맞은 값이 들어갔는지 체크!

공격!
0x10 바이트 앞뒤로 왔다갔다 하면서 올바른 위치를 잡았다.



(공격 성공)



쉘 획득!



bof 원정대의 golem을 잡아보자!

* 소스코드 분석



(golem.c)


...! 스택영역을 사용해야하는데 버퍼부터 스택 밑바닥까지 싹 초기화 시킨다.

기존의 방법으로는 접근이 안된다... 그래도 한번 실화인지 눈으로 체크해보자.



(스택 초기화)


우리가 쓰던 그리고 찾아내었던 구석구석 0으로 전부 초기화가 되었다.

여기서는 LD_PRELOAD를 제외하고는 방법이 없었다..

LD_PRELOAD란 유닉스계열에서 전역후킹을 위한 방법이다. 

예를 들어 간단한 LD_PRELOAD를 이용한 getuid 후킹 파일을 만들어보겠다.



(hook.c 파일)


getuid를 후킹하려면 그 원형과 똑같은 포멧의 함수여야한다.
return은 7777로 후킹이 되었는지 확인해본다. getuid 함수를 호출할때 이 파일이 불려나가 일을 할 것이다.

컴파일을 해준다.



(hook.so)


컴파일 방식이 조금 다르다.

그 후 export!


(export)



이제 getuid함수가 호출되면 내가 만든 hook.so가 일을 할 것이다. getuid가 불리는 대표적인 명령어 id를 사용해 보겠다.


(id 명령어)



id를 사용해보니 uid 가 7777로 나온다. 바로 내가 만든 so 파일이 일을 한 것이다.

그렇다면 이걸 여기서 어떻게 이용한단 말인가?...
물론 여기서 후킹을 이용해 my-pass 명령을 실행하여 답을 알아내는 방식이 있지만 그 방법은 불법이므로 정석대로 가볼것이다!

gdb를 통해 메모리 낮은 쪽을 찬찬히 훑다보면 LD_PRELOAD로 export한 내용이 담겨있는 것을 볼 수 있다.



(hook파일)



이 것을 이용할 것이다.
바로 hook파일에 링크를 걸어 바로 저 위치에 쉘코드를 올리고 ret주소로 저 주소를 입력하는 것이다!!!!

늘 그렇듯 경로 생성!



(경로 생성)



생성 후! 링크를 걸어준다. 바로 내가 만든 후킹파일에!


(링크 생성)



이제 export해준다.


(export)



이제 내가 건 링크 파일이 스택영역에 담길 것이고 즉 쉘코드가 올라갈 것이다.


(메모리 상황)


짠!
내가 넣은 Nop코드와 쉘코드가 보인다. 이제 Nop영역의 아무 주소나 하나 골라서 ret주소에 입력해주면 공격에 성공하게 된다.




(공격 성공!)


쉘 획득!


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

(29/500) Lord of the BOF - bugbear  (0) 2017.06.20
(28/500) Lord of the BOF - darkknight  (0) 2017.06.20
(26/500) Lord of the BOF - skeleton  (0) 2017.06.20
(25/500) Lord of the BOF - vampire  (0) 2017.06.20
(24/500) Lord of the BOF - troll  (0) 2017.06.20


bof 원정대의 vampire 를 잡아보자!

생각보다 쉬운 문제이다.

먼저 소스코드를 분석해보자!



(소스코드)



소스코드를 보면 ret 주소가 bf로 시작하되 그 다음이 ff면 안된다.

그렇다면 주소를 낮춰주어야한다. 전에 문제를 풀면서 인자가 길어지거나 프로그램명(이것도 인자니까)이 길어지면 주소가 내려가는 것을 확인할 수 있었다.

즉 이걸 이용해서 주소를 ff가 아니도록 낮춰볼 것이다. 가볍게 5000 바이트의 Nop을 달면서 시작해보자!



(Nop 5000바이트)



주소가 0xbfffe7@@ 대역으로 왔다.
많이 낮춰졌지만 우리의 목표에는 멀다 더 낮춰야한다.

100000 바이트를 줘보자!



(100000 바이트 Nop)


주소가 눈에 띄게 낮아졌다. 0xbffe@@@@ 대역으로 낮춰졌다. 이정도면 충분하니 0xbffe@@ 대역의 주소를 ret 주소로 넣고 Nop 코드 뒤에 쉘코드를 붙여서 실행하면 가볍게 공격에 성공할 수 있을 것 같다.



(공격 성공)



직접 해보니 쉘을 획득 할 수 있었다. :)


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

(27/500) Lord of the BOF - golem  (0) 2017.06.20
(26/500) Lord of the BOF - skeleton  (0) 2017.06.20
(24/500) Lord of the BOF - troll  (0) 2017.06.20
(23/500) Lord of the BOF - orge  (0) 2017.06.20
(22/500) Lord of the BOF - darkelf  (0) 2017.06.20


bof 원정대의 troll을 잡아보자!

*소스코드 분석


(troll.c)



소스코드를 보면 우리가 늘 사용하던 인자영역이 초기화 되는 코드가 추가된 것을 확인 할 수 있다.
하지만 argv[0]은 초기화가 안되는데 이점을 이용해서 전 문제에서 풀던것 처럼 풀수가 있다.

실제로 사용할 메모리 주소먼저 확인해본다.



(gdb를 통한 실행)




(초기화된 부분)



늘 사용하던 argv[1] 영역이 0으로 초기화 된것을 확인 할 수 있다.

하 지만!



(argv[0] 영역)


argv[0]영역을 보면 뭔가가 있다. 바로 프로그램명(경로) 이다! 확인해보면


(프로그램명)



프로그램 이름(경로)인 것을 확인 할 수 있다.
그러면 전에 문제처럼 링크를 걸어서!

경로 먼저 생성해준다. \x2f가 경로의 구분자로 사용되기 때문에


(경로 생성)


그 후 링크!


(링크 생성)



Nop이 200개 정도니 충분하겠지? 바로 공격해본다.



(Segmentation Fault)


Segmentation Fault가 난다.. 이런.. 확인해봐야겠다. tromy 라고 내가 복사시킨 복사본을 가지고 링크를 똑같이 생성한 후 메모리를 확인해본다.



(링크생성(복사본에게))



(메모리 모습)



아하 조금 달랐다. 프로그램 이름이 길어져서 메모리 주소가 조금 바뀌었나보다.

다시 원본에 링크를 걸어주고!



(링크 생성(원본))



(공격)



공격! 은.. 또 실패!
Segmentation Fault...
뭐지!!!! 다시 복사본으로 링크를 만든 후 core 파일을 열어보았다. ( 복사본 링크과정은 똑같아서 생략했다.)



(core 파일)



음... 위치가 또 다르네..
어쨋건 nop영역의 주소를 하나 가져다가 공격!



(공격 성공)



쉘 획득!

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

(26/500) Lord of the BOF - skeleton  (0) 2017.06.20
(25/500) Lord of the BOF - vampire  (0) 2017.06.20
(23/500) Lord of the BOF - orge  (0) 2017.06.20
(22/500) Lord of the BOF - darkelf  (0) 2017.06.20
(21/500) Lord of the BOF - wolfman  (0) 2017.06.20


bof 원정대 orge를 잡아보자!

소스코드를 분석한다.


(orge.c)



orge의 소스코드를 보면 특이한 점이 있다. 바로 argv[0]의 길이가 77이여야한다는 점이다.
argv[0]는 프로그램경로 명(프로그램이름포함) 이다.
그러므로 이 문제는 링크를 이용해서 프로그램이름 길이를 조절해야한다.

77바이트니 그 안에 쉘코드를 올리고 argv[0]주소를 넘겨주어도 이 문제를 해결할 수 있을 것이다!

먼저 프로그램 경로명의 길이 77바이트를 맞추기 위한 작업이다.
gdb를 통한 메모리 확인을 위한 것이므로 간단히 cp를 이용해 77바이트만 맞추겠다.



(경로명 77바이트)



이 경로까지 경로 길이는 총 14바이트이므로 나머지 63바이트를 채워주면 된다.

이렇게 만들어진 문제프로그램을 gdb로 열어보자.



(gdb로 실행)



(메모리 확인(인자영역))



인자 영역이 초기화 되지 않는 것을 확인 할 수 있다. 44로 채워진 공간이 프로그램 이름이다.
이 주소를 ret주소로 넘겨주고 이 영역을 NOP + 쉘코드로 만들것이다.

먼저 링크를 생성하기 위해 해야할 일이 있다.
일단한번 만들어보겠다.



(에러발생)


에러가 발생하였다.
이유는 쉘코드 안에 \x2f 가 들어있는데 이것이 아스키문자로 /  를 의미한다. 그렇기에 경로로 인식이 되어서 해당 디렉토리가 없다는 뜻이다. 그러므로 \x2f 가 들어있는 곳을 감안해서 디렉토리를 만들어준다.



(디렉토리 생성)



디렉토리를 생성할 때 -p 옵션을 주어 하위 디렉토리도 한번에 생성한다.


(링크 연결)


그 후 링크를 연결한다.
대상은 Nop(31바이트) + 쉘코드(32바이트) 이다.

자 이제 준비는 끝났다. ret 주소로 아까 확인한 0xbffffbac 로 주고 프로그램을 실행한다!



(Segmentation Fault)



??..?!

뭐지

원인을 파악하기 위해 orge를 ormy로 복사한 후 ormy를 아까 했던 대로 링크를 걸어서 gdb로 열어본다.


(링크 재생성(복사본으로))



그 후 gdb로 메모리를 확인해본다.



(메모리 확인(인자영역))



어라? 이 주소가 맞는데

프로그램을 실행시킨 후 core 파일을 확인해본다.


(core 파일)



core 파일을 확인해보니 원인을 알았다.
이유는 잘 모르겠지만 gdb를 통한 메모리 주소와 조금 다르다...
nop 위치의 아무 주소를 하나 택한 후 그 주소를 넣어준다.

다시 원본 파일에 링크 생성하여 공격 준비



(원본 파일에 링크)


그 후 아까 확인했던 nop영역의 주소 하나를 골라 입력해준다.



(공격 성공)



쉘을 획득하였다 :)


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

(25/500) Lord of the BOF - vampire  (0) 2017.06.20
(24/500) Lord of the BOF - troll  (0) 2017.06.20
(22/500) Lord of the BOF - darkelf  (0) 2017.06.20
(21/500) Lord of the BOF - wolfman  (0) 2017.06.20
(20/500) Lord of the BOF - orc  (0) 2017.06.20

bof 원정대의 darkelf를 잡아보자!

차근차근 darkelf 소스코드부터 분석해보자.



(소스코드)


추가 된 것은 인자 길이를 체크하고 있다. 게다가 인자를 1개만 넘겨줘야한다.

하나만 넘겨주는데 그 길이도 48바이트를 넘어서는 안된다.

전전 문제에서 풀었던 방법으로 하면 위 조건들을 모두 충족시킨 체로 공격에 성공시킬 수 있었다.

전략은 바로
인자 주소로 ret주소를 넘기는 것이다.
44개 바이트 뒤에 ret 주소가 오는데 여기 ret 주소에 첫번째 인자의 주소를 넘기는 것이다.
그리고 첫번째 인자에 쉘코드를 올리면된다. (아직 인자를 초기화시키지 않기 때문에 공격 가능하다.)

그렇다면 메모리 주소부터 확인해보자!



(gdb 실행)



(메모리 확인 (사용가능한 영역))



예상대로 첫번째 인자의 주소 영역은 날라가지 않았다.
여기에 쉘코드를 올리고 이 주소를 ret주소로 넘겨줄 것이다.

그냥하면 주소를 딱 맞춰야 하기 때문에 Nop 슬레이딩을 쓸것이다. 비록 44바이트 중 내가 쓸 쉘코드는 32바이트 이기 때문에 12바이트 Nop 밖에 쓰지 못하지만 그래도 유용할 것이다.



(gdb 실행)


gdb를 통해 메모리 주소를 확인해본다. 먼저 0xbffffc28 주소로 ret 주소를 넘겨준다.


(메모리 확인(ret주소))


ret 주소에 0xbffffc28 이 잘 넘어간 것을 체크!



(메모리 확인(0xbffffc28))



0xbffffc28 주소 메모리를 확인해보면 Nop코드 위치이고 여기서 Nop이 실행되면서 쉘코드까지 실행될 수 있을거라는 것을 확인 할 수 있다.

그렇다면 darkelf를 공격해보자!



(공격 성공)



바로 쉘을 획득할 수 있었다.



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

(24/500) Lord of the BOF - troll  (0) 2017.06.20
(23/500) Lord of the BOF - orge  (0) 2017.06.20
(21/500) Lord of the BOF - wolfman  (0) 2017.06.20
(20/500) Lord of the BOF - orc  (0) 2017.06.20
(19/500) Lord of the BOF - goblin  (0) 2017.05.07


BOF 원정대 ORC 를 잡아보자!

먼저 orc.c 소스코드 파일을 주었으니 열어서 확인해본다.


(orc.c 소스코드)



orc 의 특징
1. 버퍼 40바이트이다. 
2. 인자로 프로그램명을 제외하고 하나 이상을 넣어줘야한다.
3. 모든 환경변수를 0으로 초기화한다. ( 환경변수 사용X )
4. 스택영역(\xbf) 영역을 사용해야한다.

그렇다면 gdb를 이용하여 실제 메모리에 어떻게 들어가나 확인해보자.!



(gdb로 프로그램 실행)



인자를 하나 이상 넣어줘야하고 ret 영역의 주소를 bf로 시작하는 주소로 입력해야한다는 조건을 맞추어 시작했다.



메모리 상태(환경변수 초기화)


메모리 상태를 확인해보니 환경변수 있을 자리에 전부 0으로 초기화 되어있는 모습을 볼 수 있다.

그런데?..!



메모리 상태(이용할만한 공간)



바로 ebp 아래 부분에 우리가 인자로 넘겨주는 값을 저장하는 영역을 발견하였다.
이 곳에 쉘코드를 넣고 이 주소를 ret주소로 넘겨주면 쉘을 획득할 수 있을 것 같다 :)

그렇다면 앞에 44바이트 중 우리의 쉘코드(32바이트)를 넣을 것이므로 
쉘코드 + 12바이트(더미) + ret 주소 형식에 맞추어 입력할 것이다. 우리 쉘코드가 위치할 주소 두번째 인자가 저장되는 곳이 0xbffffc2f 이므로 ret 주소에 이 주소를 넣어서 실행해본다.



메모리 확인(ret주소)



ret 주소에 우리가 의도한 0xbffffc2f가 잘 들어가 있는 것 체크!




(메모리 확인(쉘코드))



0xbffffc2f에 쉘코드가 올라가 있는지 체크!

전부 딱 맞게 올라가 있다. 그러므로 이제 gdb를 나와서 직접 실행해보자!

실제로는 메모리 주소가 조금씩 차이가 날 수 있다. 그러므로 일단 core dump를 위해 우리가 복사한 프로그램으로 실행시켜본다.




(bof 시도)



역시 한방에 안됬다. core 덤프 파일을 확인해본다!

우리가 입력한 ret 주소 0xbffffc2f 주소 근처를 확인해본다.




(core dump 파일)



확인해보니 0xbffffc2f에는 다른 것이 들어가있고 우리가 원하는 쉘코드 주소는
0xbffffc32 라는 것을 알 수 있다.

이제 ret 주소에 0xbffffc32를 넣어주고 실행!



(bof 성공)



쉘을 획득 할 수 있었다.

이렇게 orc를 처치하였다. :)


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

(22/500) Lord of the BOF - darkelf  (0) 2017.06.20
(21/500) Lord of the BOF - wolfman  (0) 2017.06.20
(19/500) Lord of the BOF - goblin  (0) 2017.05.07
(18/500) Lord of the BOF - cobolt  (0) 2017.05.05
(17/500) Lord of the BOF - gremlin  (0) 2017.05.05

+ Recent posts