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

소스코드의 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



(matrix 문제)



Backdoor 워게임 사이트의 matrix문제이다다른 엉뚱한 곳 파다가 쉽게 풀수 있었던 문제를 돌아가서 풀게된 문제이다.

먼저 바이너리를 받아서 handray로 직접 c로 바꾸었다.




(main 중 구문)


위와 같이 main문에서 choice를 입력받고 이름을 입력받은 후해당 choice에 따라 함수가 분기되는 구문이 있었다.



(zion 함수)


zion이라는 함수가 system 함수를 사용하고 있고 공략할 만한 부분이었다. Snprintf를 이용해서 buffer에다가 name %s에 치환시켜 3번째 인자를 저장한다그리고 그 버퍼를 system 함수로 실행한다.



(command injection 구상)


나는 이 구문에서 sql injection과 같이  ;를 이용해서 중간에 내가 원하는 /bin/sh을 실행시킬 생각이었다즉 이름에는 나의 payload가 들어가야하고 zion이라는 함수가 실행되어야한다.
 
Zion을 실행시키기위해서는 choice 0이 되어야하는데 0이 되면 입력을 다시 받으라고 한다정상적으로 쓰지 못한다. Zion을 실행시키기 위해 프로그램을 더 분석하던 중 zion으로 넘어갈 수 있을 만한 껀덕지를 발견했다.



(blue_pill 함수 중)


Blue_pill 함수안에 있는 내용이다여기서 zion실행 문장까지 내려가려면 조건이 이름이 Neo여야하고 입력받는 배열을 7사이즈로 해야하고 A*B의 결과가 특정 값이어야한다
Neo에다가 뒤에 null을 붙이고 나의 명령 payload를 붙여서 보내야지하고 생각했다.(잘못된 생각이었고 방향을 바꾸었어야했다.)



(기대값)



A*B의 값이 위와같이 나와야한다.




(기대값 수치)



Gdb 메모리 값을 파일로 정리했다그 값을 파이썬을 이용하여 10진수로 바꿀 것이다.



(matric 출력코드)


값을 해당 배열의 위치를 고려하여 출력하였다.



(exploit)



그렇게 해서 태어난 exploit코드
A의 값에는 해당값과 B에는 곱셈의 항등원으로 7x7 단위행렬을 넣어주어 곱셈의 결과가 기대되는 행렬이 나오도록 하였다.



(실패)



Zion을 실행까지 왔다그러나… 
 
놓치고 있었던 것….
%s 또한 문자열을 인식하는거니까 name에 null까지만 넣는다

, Neo뒤에 null을 추가하고 뒤에 payload를 넣는다는 생각은 너무 짧았었다..



(메모리 상황)



생각을 바꾸어 choice값을 0으로 만들 방법을 생각했다.

마침 name바로 위에 있으니 덮어 쓸 수 있지 않을 까 생각했다.



(입력값 검증 헛점)



입력값을 name배열만큼 검열하는 듯했지만 자세히 보니 아주 작은 헛점이 있었다.
맨 마지막의 개행문자를 null로 바꾸어준다그런데 딱 해당하는 길이가 넘어오면 걸리지 않았다
즉 비교문에 =이 빠져 한바이트를 null로 덮어쓸수 있었던 것이고 zion을 실행시킬 수 있었다.



(exploit)


다시 작성한 exploit 코드



(쉘 획득)



문제 해결!

*저건 플래그가 아니고 저 파일을 읽어야합니다 :)

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

(66/500) pwnable.kr - rsa_calculator  (1) 2018.03.10
(65/500) pwnable.kr - echo2  (0) 2018.03.08
(63/500) pwnable.kr - syscall  (2) 2017.10.22
(62/500) NOE.systems - BURYBURY  (0) 2017.10.01
(61/500) NOE.systems - double_input  (0) 2017.09.17

System call을 이용해 커널모드로 전환되었을 때 root권한으로 상승시켜야 하는 문제이다.
커널 exploit을 맛볼 수 있는 기회가 되었다.



(문제화면)


커널 exploit에서는 시원하게 uid 0으로 만들어버리는 방법을 사용한다그 후 shell을 실행시키면 root권한의 쉘을 얻을 수 있는 것이다.



(시스템 확인)



접속해보았더니 시스템이 arm 시스템이었다.




(커널모듈)


시스템을 확인해보니 m.ko 커널 오브젝트 파일이 있었다m.ko 모듈을 insmod 했을 거라는 예상을 해본다
 
커널 모듈 소스코드를 문제에서 주었다.



(커널 모듈 소스코드)


확인해보면 system call을 하나 만들어서 등록해두었다입력을 통해 out으로 문자열을 소문자면 대문자로 바꾸어주어 보내는 함수이다. printk를 통해 함수가 등록된 것을 알려준다.



(dmesg 확인)



dmesg를 통해서 확인해보니 모듈이 등록되어있는 것을 확인했다.
 
위에서 정의한 system_call은 입력포인터출력포인터에 대한 범위 검사가 없다그렇기에 원하는 곳에 원하는 값을 적을 수 있다는 이야기이다.
시스템콜이 동작할 때는 커널모드로 전환되므로 메모리에서 커널영역 또한 참조하고 수정할 수 있다위 시스템콜의 취약점을 이용하면 커널영역을 마음대로 수정할 수 있다는 이야기가 된다.
나는 system call table의 주소를 내가 원하는 함수로 덮어 쓰므로써 원하는 함수를 실행하기로 계획했다
 
커널 exploit 할 때는 uid 0으로 만들어버린다고 했었는데 방법은 이렇다.
commit_creds(prepare_kernel_cred (0));
 
위 주소는 cat /proc/kallsyms | grep @@ 을 통해 찾을 수 있다
그 후 table의 원하는 주소를 찾아 그 주소를 위 함수가 실행될 수 있도록 덮어쓰고 실행하면 된다.
 

나는 getuid geteuid를 덮어썼다.



(내 exploit코드)



Exploit 코드이다.
 

여기서 주의할 점은 영어 소문자의 범위가 되면 -0x20이 된다는 점이다그렇기에 60까지 쓸 수 있는데 그러면 geteuid를 실행하게 되면 0x8003f560으로 넘어 가게 될텐데, 6c 까지 슬라이딩해서 넘어가야한다. nop으로 깔아주려 했으나 arm 계열에서 nop의 코드는 0x00이다.




(nop 코드)



그렇기에 nop의 기능과 같게 의미없는 코드로 채워야했다. 12바이트를!



(mov r1, r1)


이 코드를 컴파일 한 후 실행해보면



(권한상승)


권한이 상승된 것을 확인할 수 있고 flag까지 안전하게 확인할 수 있었다.

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

(65/500) pwnable.kr - echo2  (0) 2018.03.08
(64/500) backdoor - Enter the matrix  (0) 2017.10.31
(62/500) NOE.systems - BURYBURY  (0) 2017.10.01
(61/500) NOE.systems - double_input  (0) 2017.09.17
(60/500) pwnable.kr - crypto1  (0) 2017.08.27


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



(문제 화면)


부리부리

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



(프로그램 실행)



문제를 실행해보면 위와 같이 나온다.
기능은 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


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

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


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

문제는 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


오기로 풀어낸 문제이다.

쉘코드를 만들어주는 파이썬 모듈을 이용하면 편하다고 했지만
나는 내가 만들기 시작한거 내가 직접 만들어서 풀고싶었다.

밤을 새서 찾아보다가 자고 일어나니 리버싱에서 배웠던 기술이 생각이나 문제를 해결할 수 있었다.

asm 문제이다.



(asm)



문제 화면은 이렇다.


(문제 화면)



쉘코드를 만들으라는 것 같다.

문제는 flag 파일 이름이 너무 긴것...



(긴 flag)



sym 링크를 쓰고 싶었지만 chroot로 인해 링크 걸어봤자 소용 없었다.

즉 저 긴 문자열을 쉘코드 안에 넣어야한다는 것...
하도하도 생각하다가 리버싱 공부하면서 배운 기술이 생각났다.

바로 call !!
call 명령어로 아래로 쭉 내려간다 이때 rip를 스택에 저장하고 가는 걸 이용한느 것이다.


(구성한 모습)



뒤에는 문자열들을 입력해준다.

그렇게 되면 엉뚱한 명령어로 인식이 되는데 상관없다. 나는 call을 이용해 이녀석들을 내려갈거기 때문에



(call 사용)



call 을 사용해서 아래로 내려간다.

그런데 올리디버거에서 쓰는거랑 직접 gdb를 이용해 코드를 보니 조금 달라서 직접 asm 를 만들어 테스트해보았다.



(테스트)



테스트 하면서 안 사실!
e8 은 call 명령어 이고
뒤에 0d 만큼 떨어진 곳으로 call 하게 되는 것!

그렇다면 나는 e8 을 이용해 100 떨어진 곳으로 이동시킬 것이다.



(call binary)



그 후 살짝의 미스매치가 있으므로 Nop 코드로 쿠쎤을 깔아준다.



(쿠쎤)



그 후 pop rsi 를 시작하여 open, read, write 함수를 호출하는 코드를 붙이면 끝!




(테스트)




(테스트 성공)



테스트 성공! 기분이 좋다.



(공격도 성공)



공격도 성공하니 기분이 더더 좋다!

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

(58/500) pwnable.kr - fsb  (0) 2017.08.12
(57/500) pwnable.kr - ascii_easy  (2) 2017.08.09
(55/500) pwnable.kr - uaf  (0) 2017.07.12
(54/500) pwnable.kr - cmd1  (0) 2017.07.12
(53/500) pwnable.kr - blackjack  (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

+ Recent posts