학교 수업듣느라 틈틈이 푼 문제이다.
오랜만이다.!



(문제 화면)


부리부리

이름을 봤을 때 힙영역 취약점이라고 예상했다그리고 확인해보니 힙영역의 취약점을 이용한 문제였다.



(프로그램 실행)



문제를 실행해보면 위와 같이 나온다.
기능은 ADD Chunk를 이용해 데이터를 저장할 수 있는데 3개까지 만들 수 있다.
그리고 unlink를 통해 unlink함수를 구현해 놓았구, modify에서는 
기존의 힙의 데이터를 수정할 수 있게 해놓았다.



(전역변수)



취약하다 생각한 것은 A,B,C라는 전역변수를 만들어 두었고, malloc으로 할당받을 때 
각각 A,B,C에 주소를 저장해 놓는 것이었다

그리고 수정할 때 이 A,B,C에서 값을 가져와서 그 주소에 있는 것을 수정했다.



(unlink 취약점)



바로 이부분인데힙영역의 취약점을 일부러 unlink라는 함수를 만들어놔서 구현해 놓았다현재 버전의 시스템에서는 취약점이 막혔기 때문인 것 같다.


(heap overflow)



일단 기본적으로 힙영역 취약점을 이용하려면 heap overflow가 가능해야한다. ADD 함수에서는 전혀 힙오버플로우가 일어나지 않았고 좌절할 뻔했지만 modify가 있었다.
바로 여기서 수정을 하는데 입력을 0x100 바이트 받는다.
나는 GOT를 오버라이트 해서, A,B,C라는 전역변수에 GOT주소를 적고 싶었다그렇게 되면 수정할 때마다 GOT영역을 0x100바이트 내 맘대로 조작할 수 있기 때문이다.
위에서 말했듯 A,B,C라는 전역변수에 malloc을 저장해 두었고, A,B,C 주소 중 가장 끝의 것 C 주소를 선택했다.
 
이제 3번 청크를 수정하면 GOT영역의 데이터에 나의 데이터가 올라간다.
이제 중요한건 system 함수에 /bin/sh 이라는 문자열을 어떻게 전달할 것인가 이다.
 

나는 아주 기맥힌 방법을 생각해냈다.



(공략)



이 부분에서 idx라는 전역변수에서 값을 꺼내오고 확인한 후 puts를 실행하는 과정인데,
여기서 보면 rax를 초기화 하지 않는다.!
 

여기서puts함수를



(덮어쓸 주소)


위 주소로 덮어쓰고, free함수를 system 함수로 덮어쓰면 바로 rax에 있던 값이 system 함수 인자값으로 들어간다.
그렇다면 나는 idx값에 첫번째 청크 주소값 + 0x10 을 넣을 것이다수정할 때 0x10부터 데이터가 들어가고 나는 첫번째 청크에 /bin/sh + 0x00 을 넣을 것이기 때문이다그렇게 되면 /bin/sh 무자열 주소가 system 함수에 전달되는 것이다.
 
그런데 문제가 있었다. Modify 후 가장 먼저 실행되는 것이 puts함수였다즉 puts 함수를 위 주소로 덮어쓰면 제일 먼저 rax값을 설정하는 그 주소를 실행하지 못한다는 것이다.
, puts 함수가 실행되기 전 다른 함수가 실행되어야하고그 함수에 rax값을 설정하는 주소로 덮어써야한다.
 

나는 modify의 카나리 체크함수를 이용하기로 했다일부러 카나리값을 망치게 하는 주소를 free함수에 넣는다그리고 unlink를 실행해 주어 이 주소로 넘어가고이 때 modify에서 puts 함수와 기타 함수를 덮어쓰면 원하는대로 rax값 설정 후 puts 함수로 넘어간다.



(카나리 망치기)


바로 이 주소를 free 함수로!
 

나의 계획을 정리하면 이렇다.



(공략 정리)




(문제 해결)



Exploit 작성 후 공격 실행 성공한 모습


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

(64/500) backdoor - Enter the matrix  (0) 2017.10.31
(63/500) pwnable.kr - syscall  (2) 2017.10.22
(61/500) NOE.systems - double_input  (0) 2017.09.17
(60/500) pwnable.kr - crypto1  (0) 2017.08.27
(59/500) pwnable.kr - dragon  (0) 2017.08.17



주정도 걸려서 푼 문제이다.
이 문제를 풀기위한 고비가 크게 3번 있었다그 고비를 중심으로 설명하겠다.
 

먼저 문제 프로그램을 살펴보자



(프로그램)


이 프로그램은 입력을 2가지 방식으로 받는다. First Input에서는 스택에 입력을 저장하고, Second Input에서는 heap영역에 값을 저장한다그리고 3번 Delete로는 값을 삭제하는데 스택에 저장한 값은0으로 채우고힙영역은 free로 메모리 할당을 해제하여 삭제한다. Print는 저장했던 값을 출력하고 Exit는 프로그램을 종료한다.
 
1번째 고비
프로그램이 단순한 executable 파일이 아니다??...

이 프로그램은 shared object 파일이었다.



(파일 정보)



So 파일은 동적으로 프로그램 실행할 때 메모리에 로딩하는 파일로만 알고 있었다물론 그런 파일이지만 이 프로그램 단독으로 실행되는 줄은 몰랐다.
 
왜 이게 고비였냐 하면메인함수로 찾아가기가 힘들었다.
게다가 심볼이 없다. Start address를 찾아서 브레이크를 걸고 실행하여 일일이 뒤져가 main 함수를 찾아내었다.
 
*  Main 함수까지 가는법.
먼저 gdb로 열고 한번 그냥 실행해 주어야한다아까 말했든 이 파일은 so 파일이어서 처음에는 메모리에 존재하지 않는다… 한번 실행해주고 중단 시킨 후 재실행할 때 break를 잡아준다.

Break 잡아주는 위치는 _dl_start_user 여기로 잡아준다이건 내가 찾아낸 부분인데 이 부분 전에 starter 어쩌구 함수에서 실행이 되고 이 함수로 넘어오는데 여기에 break 잡아준다.




(main 찾아가기(1))




main 찾아가기(2)



그 후 위 사진처럼 저 위치에서 si로 함수 안으로 들어가준다.



main 찾아가기(3)



여기 call rax에서 si로 함수 안으로 들어가주면 여기서부터 main 함수이다.



main 함수



Main 함수드디어찾았다.! (여기서 첫번째 고비를 넘겼다.)

(여기 main함수 까지 찾아오는데 정말 오래걸렸다.)
 
두번째 고비
스택과 힙힙은 취약점이 없어보이는데??...
 
두번째 고비는 힙 영역에 취약점이 없어 보인다는 점이었다스택 영역은 스택 카나리가 존재해서 bof를 생각도 안했다그리고 heap영역에는 다양한 취약점이 존재한다는 걸 알고 있었기에 노려보려 했지만 쉽지 않았다.
 
그 이유는 heap영역에 취약점은 기본적으로 heap overflow에 기반했다하지만 여기서는 입력할 데이터 수를 받고 그 데이터길이만큼 malloc으로 할당해준다그리고 그 영역에 read함수를 통해서 해당 데이터 길이만 입력을 받기 때문에 overflow가 일어나기 힘들었다.
 
여기서 한참 방황한뒤 취약한 틈새를 발견하게 되었다바로 print하는 부분이다.

바로 입력한 문자열을 출력할 때 null 값까지 쭉 출력한다는 점을 이용하여 스택영역에 값들을 노출 시킬수 있겠다는 생각이 들었다.




(스택 모양)


위 빨간 영역에 바로 버퍼의 주소이다스택영역에 입력할 수 있는 길이가 130까지 되므로 충분히 카나리를 노출 시킬 수 있고 ret 주소 까지 덮어쓸 수 있다.
 

카나리를 노출시킬 수 있으면 카나리 위치에 카나리를 넣을 수 있고 그렇다면 stack overflow를 할 수 있다는 생각이 들었다.



(카나리 구하기 함수)



바로 여기서 이 부분을 파고 들기 시작했다.
 
3번째 고비
쉘을 따기 위해서는 system함수를 불러야하고 인자를 넘겨야하는데 어떻게
 
3번째 고비는 인자 문제였다.
Ret 까지 system 함수를 호출하는건 문제가 없었다.
하지만 인자를 넘겨주는데 문제가 있었다리눅스 64비트 환경에서 인자를 넘겨줄 때는 첫번째 인자는 rdi 레지스터를 이용해 넘겨준다그렇다는건 rdi 레지스터에 /bin/sh 이라는 문자열 주소를 넣고 ret으로 system을 실행시켜야하는데….
 
** 맞다.! 나는 stack smashed가 카나리 변조만으로 이루어지는 줄 알았다하지만 카나리 값을 제대로 넣어도 stack smased가 나오는 경우가 있었다바로 rsp의 위치였다리눅스 64비트에서 디버깅을 해보니 rsp 가 아무역할을 안하는거 같길래 무시했더니, rsp가 rbp보다 큰주소(아래)주소에서 놀고 있을 때 stack smashed가 일어났다함수 시작 앞에서 rsp를 sub로 조정하는 부분이 불필요한 부분은 아니었던 것이다
이 부분으로 또 한참이 걸렸다.
이 과정에서 나는 ROP를 생각했다이 때 fake rbp를 이용해 스택 포인터를 마음대로 움직여야겠다는 생각도 하였다그러면서 ROP를 해야겠다는 생각이 들었다
그래서 ROP 가젯을 찾기 위해 새로운 프로그램들을 받으면서 경험했다.
 

문제 해결에 도움을 준 프로그램은 ROPgadget이라는 프로그램이었다여기서 엄청 핵심인 가젯을 찾아주었다.



(퍼펙트 가젯)



아니…! Pop rdi !! 라니!!!!!! 게임 끝이지 뭐

Fake rbp를 이용해 스택 포인터를 버퍼쪽으로 가져간 후 여기서 pop rdi 할 때 스택 위치에 /bin/sh 문자열을 넣을 주소를 넣어주고 ret 에 system 주소를 넣어준다그리고 /bin/sh 문자열은 버퍼의 어딘가 위치에 넣고 그 offset만 맞추어서 pop rdi에 들어가게 하면 된다.


(exploit 코드)



exploit 해주면



(문제 해결)

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

(63/500) pwnable.kr - syscall  (2) 2017.10.22
(62/500) NOE.systems - BURYBURY  (0) 2017.10.01
(60/500) pwnable.kr - crypto1  (0) 2017.08.27
(59/500) pwnable.kr - dragon  (0) 2017.08.17
(58/500) pwnable.kr - fsb  (0) 2017.08.12

+ Recent posts