이번 문제는 소스코드가 주어져서 쉽게 해결할 수 있었다.
소스코드가 없었다면, 이 취약점을 찾기 쉽지 않았을 것 같다.

소스코드의 select_menu() 함수가 문제였다.


(문제 코드)



(문제코드)


문제가 되는 함수의 부분이 위의 빨간 부분이다.
select_menu() 함수는 안에서 recursive하게 자기 자신을 호출한다.

화면에 다시 메뉴를 출력하게 할 때, while 문이나 다른 코드로 처리를 하는게 보통인데
참 특이하게 자기자신을 다시 호출하므로써 메뉴를 다시 출력하였다.

재귀적 코드는 깔끔해 보이는 장점이 있지만,
재귀가 많이 반복될 수록 스택 메모리를 많이 잡아먹는 현상이 일어난다.

그래도 함수 마지막에 호출하게하여 Tail Call(Tail Recursion)을 유도했지만,
실제로 gdb로 분석해본 결과 스택메모리를 낭비하며 동작하고 있었다.

그렇다면 스택을 쭉 낮출 수 있다는 것이다.
그리고 랜덤하게 주소를 받는데 그 주소가 스택 주소가 아니란 법도 없다.
더군다나, 여기서 만든 메모리할당 함수를 보면 실행권한까지 떡하니 줘버린다.
쉘코드를 써달라고 애원하는 듯 하다.

내가 exploit을 짜기 전 계획이다.
1. 스택영역의 주소를 얻는다.(계속 반복하여서)
2. 스택을 차곡차곡 쌓아서 주소를 팍 낮춘다. 우리가 구한 스택영역의 주소보다 낮게.
3. 그 다음 메모리할당을 한번 더 받아서 그 안에 쉘코드를 넣어둔다.(이 때 받은 쉘코드의 주소를 기억)
4. 아까 받은 스택영역의 주소에다가 쉘코드주소를 쫙 넣어둔다. 4096바이트만큼 쓸수 있으므로 1024개의 주소로 덮어쓸 수 있다.
5. exit 을 실행하면 스택이 recursive하게 리턴되면서 그 중간에 덮어쓰여진 쉘코드 주소로 뛰게 되고 쉘코드 실행!

exploit을 짜면서 바뀐 계획인데

2번의 과정은 없어도 되었다. 스택영역의 주소를 얻기위해 반복하는 과정에서 자연스레 엄청 낮아졌다.



(스택 영역 주소 구하기)


반복은 받은 주소가 현재 ebp 주소보다 커진 경우까지 반복한다.

그렇게 되면 ret,leave 하면서 쭉 내려갈 테니까!



(clear 함수)


반복하다보면 255개의 배열을 다 쓴다..
그런 경우 255개를 다시 다 지워주는 함수를 만들었다.
(계속 반복 할 수 있도록)

쉘코드 올리기


(쉘코드 올리기)


쉘코드를 올리는 부분이다.



(스택 영역 주소 덮기)


그 후 쉘코드 주소를 스택영역 주소에 덮어쓴다.

그리고 

exploit!



(스택 주소 받기)


엄청난 양을 받는다.

옆에 Stacking 옆의 값은 현재 ebp 값이다.


(clear 중)


clear 도 몇번 실행되었다.



(exploit)


끝내 만족하는 스택영역 주소를 받고
ret 주소를 덮어썼다.

여기서 5를 눌러 exit를 실행하면 최종적으로 shell을 획득할 수 있다.


(쉘 획득)


문제 풀이 끝!

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

(66/500) pwnable.kr - rsa_calculator  (1) 2018.03.10
(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


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

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


오랜만에 해결된 문제이다.

crypto1 문제로 암호학 문제이다.



(crypto1 문제 화면)



암호방식은 AES-CBC 방식으로 암호화된 내용을 주고받는 프로그램인데 결론적으로 admin으로 로그인 하는 것이다기사공부때 공부했던 AES 암호 방식 알고리즘을 차근차근 다시 뜯어보게 된 계기가 된 문제이다

누가 AES 암호화 특징을 이용해서 풀었다고 했다...
그래서 AES 암호방식을 공부하고 키를 찾으려했지만 AES 특징이라기보다는 블럭암호의 특징이라고 하는게 맞을것 같다...
덕분에 AES 암호 알고리즘 과정을 뒤져보는 시간을 갖게 된거 같다...ㅜ

 

해당 프로그램은 클라이언트와 서버 두개로 이루어져있다클라이언트 프로그램을 먼저 분석해보았다.



(클라이언트 프로그램)



(클라이언트 프로그램)



클라이언트 프로그램은 아이디와 비밀번호를 입력받아서 특정형태의 문자열을 만든 후 그 문자열을 AES암호화 과정을 한 패킷을 전송한다여기서 이 패킷을 보내기 전에 출력해준다.
특정 형태란 ID-PW-Cookie 이런식으로 사이에 대쉬(-) 를 입력하여 문자열을 구성해준다.
 
서버로 입력을 보낸 결과가 admin이 아니라면 프로그램이 중간에 종료된다.
admin으로 로그인해야 된다고 생각이 들었다.
 

이제 서버 프로그램을 분석해보았다



(서버 프로그램)



서버 프로그램에서는 전달받은 내용을 복호화한 후 아이디와 쿠키값을 연결하여 SHA-256으로 해쉬한 값이 pw와 일치하는지 확인한다
여기서 알 수 있는 것은 SHA-256(id+cookie) = pw 라는 것이다.
비밀번호를 알기 위해 우리는 cookie 값만 알면 된다.
 
여기서 많은 삽질이 있었다쿠키 값을 찾기 위해 AES 키를 찾으려고 했다그 과정에서 암호화 알고리즘을 분석하게 되었고 알게 된 것이 키를 알아내기 쉽지 않다는 것이었다다른 방법으로 접근 해보게 되었다.
바이트 단위로 노출하기!
보면 id-pw-cookie 이렇게 문자열이 구성된 후 암호화가 이루어진다.
만약 여기서 id에 –  13개를 입력하고 비밀번호를 입력하지 않는다면
Cookie 앞에 ‘-‘  15개 입력이 된다. AES 16바이트씩 블록으로 암호화가 이루어지므로 첫번째 블록은 쿠키의 한글자가 포함되어 “-“*15 + “쿠키 첫글자” 이런식으로 암호화가 된다.
그렇게 되면 여기서 우리는 가능한 글자를 대입해 보아 첫글자를 맞출 수 있다.
 
 

이런식으로 한글자씩 노출시키는 것이다.



(exploit 코드 일부)



쿠키의 앞 14자리를  찾는 코드이다이런식으로 한자리씩 맞추어 찾을 수 있다

(전체 exploit 코드는 공개하지 않겠습니다. 궁금하시면 글 주세요.)



(쿠키값)



Exploit 코드를 이용해 찾은 쿠키값을 찾았다.




(pw를 생성하기 위한 코드)



이제 쿠키와 id를 이어 SHA 해쉬해준다.

그 값을 pw로 admin으로 로그인 하면 플래그가 나온다.



(문제 해결)

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

(62/500) NOE.systems - BURYBURY  (0) 2017.10.01
(61/500) NOE.systems - double_input  (0) 2017.09.17
(59/500) pwnable.kr - dragon  (0) 2017.08.17
(58/500) pwnable.kr - fsb  (0) 2017.08.12
(57/500) pwnable.kr - ascii_easy  (2) 2017.08.09


정말 오래(?) 걸렸던 문제이다.

하루종일 문제푼것은 아니지만 조금씩 풀었는데 그래서그런지 오래걸린 문제이다.



(dragon 문제)




(문제화면)


문제 화면을 보면 게임을 준다.

게임을 다운로드해서 실행해본다.




(dragon 게임 실행)



직업을 고르고 용과 싸우는 게임이다.
이길 수 없다...

gdb로 분석을 시작했다.
main 함수에는 입출력 버퍼 설정하고 PlayGame 함수로 넘어간다.

그러므로 PlayGame 함수부터 분석해보겠다.



(PlayGame 함수)



함수를 보니 SecretLevel 함수?가 있다.
직업을 고를 때 3을 입력하면 이 함수로 넘어갈 수 있다.

그러면 SecretLevel에서는 뭘 할까?


(SecretLevel 함수)


비밀번호?를 입력 받고 문자열과 비교해서 일치하면 쉘을 던져준다...!

그래서 입력 문자열포멧과 비밀번호를 확인해보니



(입력 포멧, 입력해야할 문자열)



scanf 로 10글자를 받는데,,,
입력해야할 글자는 10글자가 훨씬 넘는다

즉 불가능.! 이다.
(그러면 저 문자열 10번째 다음 바이트를 00으로 바꿔서 해보자라는 생각이 들었는데, 저 문자열은 코드영역에 있기 때문에 쓰기 권한이 없어 불가능했다..)

그러므로 우리가 쓸수있는 방법은 바로 이 system("/bin/sh") 이 실행되도록 0x08048dbf 이 주소로 실행 흐름을 돌리면 된다.



(우리가 사용할 쉘)



여기서 이제 함수 하나하나 분석을 하는데 오래걸렸다...

FightDragon함수를 보면 malloc과 free를 2번씩 사용하는 모습을 확인하고 uaf 취약점이 아닐까 생각이 들었다.

그런데 uaf 취약점이든 bof든 무슨 취약점이든 입력할 곳이 있어야하는거 아닌가?...
전부 scanf  %d 로 입력을 받고 우리가 뭐 어떤 값을 입력할 곳이 없는 것이다

그러던 중 FightDragon 함수 아래쪽에서 문제 해결의 핵심을 찾았다.



(문제의 핵심)



이 부분의 scanf는 조금 특별했다.

위에서 malloc을 해주고 그 곳에 입력을 받고 게다가!



(16글자)



더구나 16글자를 입력 받는다!!!
이 부분에서 우리의 payload를 넣을 수 있다는 생각이 들었다.

그러면 어떻게 해야하나 위로 올라가보니



(공격함수)



공격함수 끝난 후 eax 값을 ebp-0x18을 넣고



(비교)



ebp-0x18 값이 0이 아니어야 입력할 기회가 주어진다.

즉, 용을 잡아야 기회가 주어진다.

또 여기서 시간을 많이 썼다.
용을 어떻게 잡는담... 인터넷에서 힌트를 좀 받았다. dragon 구조체를 잘 보면 된다고 나왔다.

dragon 구조체를 몇일을 들여다 보다 무릎을 탁 쳤다.




(dragon 구조체)



dragon 구조체를 보면
처음에는 몬스터 정보를 출력하는 함수 주소.
두번째는 몬스터 체력과 그 옆에 5가 체력회복량이다.
여기서 잘 보면 몬스터 체력이 바이트 단위로 설정되어있다. C언어로 표현하면 char자료형 처럼 1바이트 짜리이다.
범위로는 -127~127 이다.

엄마용이 80체력에 공격력이 10이니깐 계속 공격안하고 버티면 127을 넘길수 있을거 같다.
그러면 음수 값이 되고 용이 죽는다?

테스트!


(테스트 성공)



정말 127이 넘으니 용이 죽었다.
그리고 16바이트 입력을 넣을 수 있는 기회가 주어졌다.

그러면 취약점은?
scanf 로 입력받고 아래 코드를 보니 call eax 가 보인다.

보니 dragon 구조체가 free 된걸 까먹은건지 dragon 구조체의 첫번째 인자인 몬스터정보 출력 함수를 호출하고 있다.



(취약점)



내가 16바이트 입력한 곳이 방금 malloc 으로 받은 곳이니 이 주소가 전에 free 되었던 힙영역 주소이다.
같은 크기의 메모리 할당이니 전에 사용했던 그 주소가 재사용 된다.
확인해보자.! 
이름을 AAAA로 입력하였다. 그러면 처음 4바이트가 0x41414141 가 될것이고 아래 call eax에서

이 주소를 몬스터정보 출력 함수인줄 알고 이 주소를 호출하려 할 것이다.


(테스트)



AAAA 입력!




(확인)


테스트가 확인되었다.

그렇다면? 입력을 해줄 때 아까 우리가 봤던 SecretLevel에서의 쉘을 실행시키는 부분 주소를 입력해주면 된다.

pwn 모듈을 이용해 작성했다.



(exploit)



그리고 공격!



(쉘획득)



쉘을 획득하였다.!

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

(61/500) NOE.systems - double_input  (0) 2017.09.17
(60/500) pwnable.kr - crypto1  (0) 2017.08.27
(58/500) pwnable.kr - fsb  (0) 2017.08.12
(57/500) pwnable.kr - ascii_easy  (2) 2017.08.09
(56/500) pwnable.kr - asm  (0) 2017.07.14


오늘 해결한 문제는 fsb 문제!



(fsb)



FSB가 뭘까 했는데 검색해보니 Format String Bug의 줄임말이었다.
포멧스트링 공격이라..!
기사 공부하면서 배웠던 내용이지만 직접 해보는 건 처음이라 다시 많이 찾아보면서 공부하였다.

단순히 메모리값을 노출시키는데 그치지않고 값 또한 변조할 수 있다는 사실을 알았다.

문제의 소스코드!



(소스코드)



소스코드를 보니 pw와 key가 같으면 쉘을 던져준다.
pw와 key를 같게 해야하니 key를 변조 시키면 되겠다 라는 생각이 들었다.
key는 /dev/urandom 에서 랜덤한 값을 8바이트 가져온다.

fsb 공격의 기회를 4번 주는데 풀면서 이해했는데 왜 4번을 준지 이해했다.
GOT를 overwrite할 수도 있지만 4번을 준 이유는 pw와 key를 같게 하게 해서 풀으라는 문제 출제자의 의도인듯 했다.

처음에 당황했던 것은 찾아보면서 공부할 때는 버퍼가 모두 스택에 있었던 경우라서 버퍼에 직접 주소를 입력할 수 있고 그 주소를 직접 덮어썻는데 문제에서는 버퍼가 전역변수로 설정되어있어 스택에 존재하지 않는다...

그렇기에 스택에 key값의 버퍼 주소를 직접 적어주고 그 주소를 이용해 key값을 덮어 써야한다.
key의 자료형은 unsigned long long 이기에 8바이트 한번에 4바이트씩 덮어 쓸수 있으므로 2번 해야한다. 따라서 4번의 공격 기회를 준것이다.

스택에서 사용할 수 있는 주소를 보자!



(사용할 수 있는 주소)



위 사진을 보면 우리가 쓸만한 변수 주소가 보인다.

0xffedb0 주소에 먼저 key값의 주소를 적을 것이다.



(key값 주소 적기)



포멧스트링 공격을 하고 메모리를 살펴보면



(fsb공격 후 메모리)



key 주소가 덮여 쓰여진 것을 확인 할 수 있다.




(key주소)


key는 총 8바이트 크기의 자료형이므로 그 다음 4바이트도 덮어써줘야하므로 
key주소+4바이트 주소를 적어준다.



(완성)



완성된 주소를 볼 수 있다.
이제 두 주소를 이용해 원하는 값을 적고 pw입력할 때 그 값을 입력할 것이다.

처음에는 0101010101010101 를 입력하려고 했다.



(key 덮어쓰기)



이렇게 덮어쓰면 key 첫 4바이트에 01010101 이 덮어쓰여지는 것을 볼 수 있다.


(key 변조)



이렇게 그 다음 4바이트도 덮어 쓸 수 있다.

하지만 pw를 입력하는데에 문제가 있었다.
입력할 때 strtoull 함수를 이용한다.
입력한 수를 코드를 보면 인자로 10을 주어 입력된 문자열을 10진수로 인식하여 unsigned long long 자료형에 맞추어 숫자로 바꾸어준다.

그런데 어셈블 코드가 좀 수상했다.



(strtoull 근방 코드)



SAR ? 
31바이트를 오른쪽으로 움직인다. 부호를 신경써서. 즉 0이나 1 밖에 나올 수 없다.

0101010101010101 를 만들기 위해 값을 입력해보겠다.



(테스트)



eax와 edx에 값이 잘 들어가는 것을 볼수 있다.



(잘 들어간 값)



그런데 그 뒤 코드들 때문에 4바이트가 날아간다...




(0이 되버림)



음수를 입력하면 -1이 되고 양수를 입력하면 0 이 된다.

즉,,, key 주소 다음 4바이트에 있는  값이 0이되거나 ffffffff 이 되야한다.

검색을 해보던 중 해결할 만한 방법을 찾아냈다.



(0 입력)



%20$n

20번째 위치 인자 주소에 출력된 바이트 수를 입력한다. 여기서는 출력되는게 없으므로 0바이트!



(입력된 0바이트)



0바이트가 입력된 것을 확인 할 수 있다.

key값을 전부 0으로 만들고 0을 입력할 것이다.!

자! 그럼 공격을 해보자!



(key 주소 입력)



(key+4 주소 입력)



주소를 입력하고 그 주소에 있는 값을 0으로 덮어쓴다.



(key 값)



(key+4 값)



그 후 0을 입력하면 성공!~



(성공!)



쉘을 획득했다.


(문제 해결)

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

(60/500) pwnable.kr - crypto1  (0) 2017.08.27
(59/500) pwnable.kr - dragon  (0) 2017.08.17
(57/500) pwnable.kr - ascii_easy  (2) 2017.08.09
(56/500) pwnable.kr - asm  (0) 2017.07.14
(55/500) pwnable.kr - uaf  (0) 2017.07.12


여행 다녀온 후 오랜만에 푼 문제이다.

문제는 ascii_easy !

문제 이름대로 쉬운? (다른문제에 비해)




(ascii_easy)



문제내용은 이렇다.



(문제 내용)



일단 접속하여 소스코드부터 확인하였다.




(소스코드)



소스코드를 보니 아스키문자인지 체크하는 함수가 들어있다.
인자로 아스키문자가 아니면 쫓겨난다.

그리고 취약한 함수를 대놓고 vuln 이름으로 만들어놓았다. 
이 부분을 버퍼 오버플로우하는 거구만! (다행히 스택카나리가 적용되어있지 않았다.)

그리고 주목되는? 부분이 있었다.



(중요부분)



???
libc-2.15.so 파일을 가져다가 통째로 메모리에 쿵 올린다.

mmap 함수를 gdb로 동작확인해보겠다.



(mmap 함수 인자)



인자를 보니 파일 내용이 덤프될 주소 0x5555e000 이 보인다.

함수를 실행하여 덤프된 주소를 보자!



(덤프된 내용)



처음에는 데이터가 매핑이 이상하게 되었나 싶었다. (여기서 조금 헤맸다.)
(위의 캡쳐 화면은 내가 so 파일을 헥스로 수정하여 그대로 저장시킨 값이다. 그래서 위처럼 나온것)

아래 내용은 hex모드로 so 파일을 연 것이다.



(so 파일)



여기서 30303030... 이 뭘까 싶었는데 내가 헥스로 수정하고 다시 제대로 저장해야하는데 그대로 저장을 시켜서 위 화면대로 저장이 된것이다... 그렇기 때문에 아래와 같이

00000000: ~~~~ 헥스모드에 나와있는 텍스트가 그대로 메모리에 올라간 것이다...


(헥스모드 상황)



그렇군! 여기서 파일의 데이터가 떡하니 올라가는 것을 확인했으니 그 다음부터는 고민 없이
이 파일에 Nop 코드와 쉘코드를 넣어주어 bof 시킨 후 ret 주소에 해당 쉘코드 주소를 넣어주면 된다.

사실 여기서 Nop 코드 없이 정확히 쉘코드 주소를 맞출 수 있기 때문에 필요없긴 하지만
그냥 넣었다.

테스트!
놉코드 100개와 A 10개를 넣어보았다.




(테스트!)



그 후 메모리 상황!



(메모리 모습)



그렇다면 내가 만들수 있는 아스키문자중 가장 작은 값이
55562121 -> UV!! 

이다. 그렇기에 해당 주소까지 놉이 몇개 필요한지 계산해본다.




(Nop 개수)



16진수로 4121 
10진수로 16673개가 필요하다.

놉코드를 4개 더 추가시켜서 ( 놉코드 안에 안착시키기 위해)

그 뒤에 쉘코드를 이어붙여준다.



(so 파일 구성)



이렇게 하고 UV!! (55562121) 주소로 가보면



(쉘코드)



놉코드와 쉘코드를 만날 수 있다.

이제 원본 파일에 링크를 걸어서 실행할 것이다.(원본파일에 setuid가 걸려있기 때문에!)

취약 함수를 오버플로우 시켜 (더미 32개) ret 주소에 UV!! 를 넣어준다. (엔디안에 맞춰서)



(공격 성공!)



쉘을 획득하였다.

의도된 풀이를 보니 execve 함수를 이용하여 풀게 되어있다. execve 함수 주소를 보니 우리가 구성할 수 있는 아스키코드 주소로 되어있는 걸 확인 할 수 있다.



(의도된 방법)

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

(59/500) pwnable.kr - dragon  (0) 2017.08.17
(58/500) pwnable.kr - fsb  (0) 2017.08.12
(56/500) pwnable.kr - asm  (0) 2017.07.14
(55/500) pwnable.kr - uaf  (0) 2017.07.12
(54/500) pwnable.kr - cmd1  (0) 2017.07.12


UAF!!
새로 배운 취약점이다.
Use After Free
검색해보니 유명한 취약점이었다.

(몰랐다니...)

검색해서 공부한 후 문제를 풀어보았다. 

uaf 문제이다.


(uaf)



문제 화면은 아래와 같다.


(uaf 문제 화면)



Use After Free 버그를 아냐고 물어본다.

Use After Free 버그란 힙영역에서 일어나는 버그이다.
운영체제의 최적화를 위해 운영체제는 힙에 할당하고 해제한 영역을 flag 비트만 설정해둔 뒤 그 영역의 메모리 데이터를 그대로 남겨둔다.
해제한 힙 영역을 재사용 함으로써 문제가 발생한다.
특히 객체지향 언어인 JAVA, C++ 등 에서 더 강력히 발생한다.

이유는 동적으로 힙영역에 객체를 생성하고 해제하게 되면 그 객체 데이터가 힙 영역 메모리에 그대로 남아있기 때문이다.  객체안에 함수가 있다면 그 영역에 함수 포인터가 존재하는데 만약 객체 해제 후 그 객체의 함수를 재사용하는 코드가 있다면 그 영역의 객체 함수 포인터를 내가 원하는 값으로 덮어 씌워서 프로그램의 실행을 움직 일 수 있다.

소스코드를 보면서 전략을 다시 정리해보자!



(소스코드)



체크할 것은 Use After Free 함수의 취약점 조건은
1. 동적으로 메모리를 할당한다. ( 위 코드는 new라는 함수를 이용해 힙영역에 객체를 만든다. )
2. 메모리를 해제한다. ( free, 여기서는 delete로 메모리 해제한다. )
3. 힙 영역에 다시 메모리를 할당한다. ( 2를 입력하면 new를 이용해 메모리 할당한다. )

조건은 충족됬다.
이 프로그램은 실행하자마자 남자, 여자라는 객체를 생성하므로 메모리 해제 후 우리의 데이터를 힙 영역에 넣는다. 넣으면서 함수 주소를 덮어쓸 것이다. (아직 메모리 확인을 안했으므로 여기까지 계획을 세우고 조금 이따가 구체적으로 생각해보자)



(우리가 불러낼 함수)



남자, 여자 객체는 Human에게서 상속받는다. 즉 남자, 여자 객체 안에는 우리가 불러낼 give_shell 함수를 가지고 있는 것이다.



(덮어쓸 함수)



우리가 덮어쓴 후
객체의 함수를 호출해야하는데 위의 introduce를 불러낼 수 있으므로(1번을 누르면)
introduce 함수 주소에 give_shell 함수 주소를 넣을 것이다.

gdb를 이용해 메모리를 확인해보자.
우선 분기문 case 1: 모양이 보인다.



(case 1)



case 1 이므로
introduce 함수를 불러내는 코드이다.
브레이크를 걸고 실행해보면서 introduce 함수를 어떻게 부르는지 알아보자.


(함수 호출)



rbp-0x38 에서 값을 가져와 rax에 넣는다.
그 후 rax주소에 있는 값을 가져와 8을 더하고 함수를 호출한다.

여기서 rax는 0x401570 이었다.



(0x401570 주소)



0x401570 이 주소에서 8을 더한 위치의 값을 빨간색으로 표시했다.
0x004012d2에 뭐가있나 보면


(0x004012d2)



introduce 함수가 보인다.

그렇다면 -8 위치인 0x0040117a 주소를 확인해보자



(0x0040117a)



우리가 원하던 give_shell 함수 주소이다.

정리를 하면
rbp-0x38 주소는 0x00614c50 이고
여기서 가져온 값이 0x401570이다. 만약 이 값이 0x401570-8이라면 
가져와서 8더한 값이 0x401570일테고 우리의 give_shell 함수가 실행될 수 있다.

즉! 0x00614c50 위치에 0x401570-8가 되면 된다.


(덮어쓸 위치)



내가 A 4개를 입력한 파일을 이용하여 데이터를 2번 쓰니 저 위치를 덮어쓰게 되었다.

이제 공격을 해보자.
 0x401570-8 를 파일에 넣어둔다.



(파일 생성)


그 후 free 해준 뒤 데이터를 2번 써준다. (그래야 우리가 원했던 위치에 값이 올라가므로 -> 이 사실은 내가 gdb를 이용해 실험을 해봐서 나온 결과이다.)


(공격 성공)



해당 쉘을 획득 할 수 있었다.

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

(57/500) pwnable.kr - ascii_easy  (2) 2017.08.09
(56/500) pwnable.kr - asm  (0) 2017.07.14
(54/500) pwnable.kr - cmd1  (0) 2017.07.12
(53/500) pwnable.kr - blackjack  (0) 2017.07.12
(52/500) pwnable.kr - coin1  (0) 2017.07.12


leg 라는 문제이다.

지금껏 Intel 기반의 어셈블리를 보았는데
이번에는 다른 기반의 아키텍쳐 ARM을 보게된 계기가 되었다.



(leg 문제)



leg 문제 화면이다.



(leg문제 화면)



들어가보면 leg라는 프로그램과 flag 파일이 있다.



(leg 문제 접속)


처음보는 ARM 어셈블리지만 함수의 리턴값이 어떻게 넘어오는지 확인하기 위해 소스 asm 코드를 보았다.



(leg.asm)



함수를 호출하고 r0 레지스터의 값을 옮겨 담고있는 모습을 포착하였다.
r0 레지스터에 리턴값을 던지는 것 같다.

그렇다면 우리는 이제 r0를 추적하면 된다.



(key1)



먼저 Key1
pc 값을 r3에 넣고 그 값을 r0에 넣는다.
pc라는 것을 찾아보니 Program Counter 레지스터라고 다음 실행할 명령어의 주소를 담고 있는 레지스터라고 되어있다.
그렇다면 pc를 담고 있는 명령어에서 다음 주소는 8ce0 이므로 key1 = 8ce0

그 다음 Key2


(Key2)



pc값을 r3에 넣고 4를 더한다.
즉 r0 = 8d06 + 4

마지막 Key3


(key3)



lr 이라는 것을 살펴보니 함수가 호출되기 전에 복귀 주소를 lr 레지스터에 저장하고 넘어온다고 한다.
ARM 에서는 함수 호출할때 스택을 쓰지않고 lr 레지스터를 이용하나보다.



(lr 값)



복귀 주소를 확인!

이 값을 전부 더해주면 끝!



(오답)



틀렸다...

아무리 생각해도 맞는데.! 틀린게 있다면 pc값 계산이었을까?

찾아보니 ARM에서 pc값 계산이 조금 다르다.
ARM에서는 명령실행할때 단계가 4단계가 된다.
fetch -> decode -> execute -> write

예를 들어 1 -> 2 -> 3 이라는 명령어가 있고
1 명령어가 execute 단계라면
그 다음 명령어 2는 decode 단계에 있고
그 다다음 명령어 3는 fetch 단계에 들어서게 된다.

pc는 fetch 할 주소를 담고 있다.

key1의 pc 값 계산을 다시보면



(key1 pc값)



0x00008cdc 의 주소가 실행 단계에서 pc값을 r3에 넣는 것이다.
여기서 0x00008ce0 주소는 decode 단계이고
0x00008ce4 의 주소가 fetch 될 주소이다. 
그러므로 fetch될 주소인 0x00008ce4의 주소가 pc값이 된다.

이런식으로 계산을 하게 되면 정답을 찾을 수 있게 된다.



(문제 해결)

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

(51/500) pwnable.kr - shellshock  (0) 2017.07.11
(50/500) pwnable.kr - mistake  (0) 2017.07.10
(48/500) pwnable.kr - random  (0) 2017.07.06
(47/500) - pwnable.kr - passcode  (0) 2017.07.06
(46/500) - pwnable.kr - flag  (0) 2017.07.05


flag 문제이다!

문제를 풀다보면서 느낀거지만 조금 이 사이트 문제들이 짜임새 있다.
한단계 나가면서 조금씩 알아가는 느낌이 든다. :)

flag 문제 클릭!


(flag 문제)



문제를 보자


(문제 화면)



파파가 포장이된 선물을 주었다.
packed 라는 표현을 봐서 packing 된 실행파일인듯 하다. (느낌에)

일단 파일을 받아보자!



(파일 확인)



파일을 readelf 로 확인해보니 섹션이 없다.
패킹 되어있는 듯하다.

헥스 에디터로 확인해본다!



(바이너리 확인)



UPX로 패킹되어있는 것을 알 수 있었다.

먼저 UPX로 언패킹 해준다.



(언패킹)



그리고 다시 한번 확인해보자.



(flag 파일)



섹션 내용들이 자세히 나온다.
이제 디버깅 할수 있게 되었다.
그리고! 캡쳐는 깜빡했지만 실행하게되면
malloc을 실행하고 그 공간에 flag를 strcpy 할거야~ 라는 힌트가 나온다.

gdb를 열어 확인!


(main)


main 함수를 보니 malloc 함수가 호출되고 아래에 함수가 하나 더 호출된다.
함수가 하나밖에 없으니 보나마나다 이게 strcpy이다.

그러면 strpy하기 전에 break 하기!



(break 걸기)


자 이제 실행 한 후
실행 전 rax [eax] 를 확인한다.
-> 64비트에서는 함수 인자를 레지스터에 담아 넘기기 때문이다.
즉 rax 위치에 flag들이 복사될 것이다.   




(복사될 곳 확인)



strcpy까지 실행 후 이 주소의 값들을 보면 뭔가 채워져있다.



(flag)


이 값들을 string으로 보면 flag를 확인 할 수 있다.



(문제 해결)

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

(48/500) pwnable.kr - random  (0) 2017.07.06
(47/500) - pwnable.kr - passcode  (0) 2017.07.06
(45/500) - pwnable.kr - bof  (0) 2017.07.05
(44/500) - pwnable.kr - collision  (0) 2017.07.05
(43/500) - pwnable.kr - fd  (0) 2017.07.05

+ Recent posts