- 참치군
- ?
- stalk.io
- :: 2013년, 스리는 여섯살
- 웹 강좌
- 점프 투 파이썬
- 요니나의 대학생 재테크
- This is CS50
- 애자일 이야기
- isao의 IT,게임번역소
- 소프트웨어 이야기
- Color Scripter
- 어디를 가든지 마음을 다해 가라
- VisuAlgo
- 서울대 평생교육원
- 몽환
- RegExr: Learn, Build, & Test R…
- Hello, Stranger :D
- I Like Exploit
- Z3alous Security Story
- Project Euler
- Blog
- pieces of code
- window 쪼물딱 거리기
- IT - Informatics Alphabet
- rop
- 국제 정보교육센터 I2sec 대구 1기
- This is the moment. :)
- blackmoon
- z3alous는 세상에 소리 z3alous~
- Acord
- FORENSIC-PROOF
- 어셈블리
- Outsider's Dev Story
- Open Tutorials
- 코드라이언
- 컴퓨터 그래픽스와 3D 프린팅
- HACKABILITY
- Lee, Jae-Hong
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 공간복잡도
- Debug
- 오지총
- 블루블랙
- Calling Convention
- 호출규약
- 피보나치
- Hello World
- 소켓
- 탈색
- BOF
- 펌
- 파이썬
- 알고리즘
- 베이스
- 디버깅
- 버퍼오버플로우
- 창의공학설계
- 추상데이터타입
- Packet
- C언어
- ubuntu
- 염색
- 발표
- Visual Studio
- 레지스터
- 시간복잡도
- Wireshark
- 컴파일러
- 동대구
- Today
- Total
c0smicb0y
[Lord of BOF] Level 14 (bugbear -> giant) 본문
1. 문제의 힌트 확인
ls 명령어를 통해서 디렉토리의 파일 목록을 살펴보자.
[그림1] bugbear 계정의 디렉토리 목록
suid가 걸려있는 giant에서 쉘을 작동시키면 될 것이다. giant의 소스 코드를 살펴보자.
[그림2] giant의 소스 코드
이번 소스 코드는 약간 길다. 힌트에 RTL2라고 적혀있다. RTL은 Level13에서 다뤘으므로 기본지식은 똑같다.
2. RTL (Return To Libc)
이 기법은 일반적인 BOF로 스택에서 코드가 실행되는 것을 막은 non-executable stack 보호기법이 나오고 나서 제안된 방법이다. return address를 libc 영역(공유라이브러리)으로 점프시켜서 원하는 libc함수를 실행한다. 예를 들어 printf함수를 사용했다고 하면 실제로 이 함수를 정의하고 있는 /lib/libc.so.6파일이 이 영역에 적재된다. 여기서 중요한 점은 printf함수 하나만 사용을 하더라고 /lib/libc.so.6파일이 통째로 메모리에 적재된다. 그러므로 이 파일에서 원하는 함수의 주소를 끄집어내서 RET주소에 넣어주게 되면 그 함수를 실행할 수 있다. 간단하게 그림으로 나타내면 이렇다.
[그림3] RTL 기법
3. 실제
giant의 소스 코드를 다시 살펴보자.
[그림4] giant의 소스 코드
일단 인자가 없으면 argv error를 출력하고 프로그램을 종료한다. 인자가 있을 경우에는 /usr/bin/ldd에 /home/giant/assassin을 인자로 넣은 출력으로 /bin/grep libc을 수행하고 그 출력을 /bin/awk에 '{print $4}' 옵션을 걸어서 수행한 것을 fp에 넣어서 그것을 buffer에 넣고 그것을 lib_addr에 넣는다.
/usr/bin/ldd는 해당 프로그램이 사용하는 공유 라이브러리를 출력하는 명령이고 bin/grep은 입력에서 인자에 있는 문자열을 찾는 명령이다. 그리고 /bin/awk는 입력을 토큰화 시켜서 옵션에 따라 출력하는 명령인데 여기선 '{print $4}'을 통해 네 번째 토큰이 출력되게 되어있다.
그리고 동일하게 /usr/bin/nm에 /lib/libc.so.6을 인자로 넣고 그 출력으로 /bin/grep __execve를 하고 bin/awk를 통해 첫 번째 토큰을 출력하게 되어 있다. 이것을 또 buffer에 넣어서 execve_offset에 넣는다.
/usr/bin/nm은 라이브러리, 컴파일 된 오브젝트 모듈, 공유 오브젝트 파일, 독립 실행 파일 등의 바이너리 파일을 검사해서 그 파일들에 저장된 내용 또는 메타 정보를 표시하는 명령이다.
그리고 lib_addr과 execve_offset을 합하여 execve_addr에 넣고, ret에 첫 번째 입력 인자의 44번째부터 4바이트를 복사한다. 이 값이 evecve_addr과 같은지 확인한다. 다르면 You must use execve!를 출력하고 프로그램을 종료하고, 같으면 BOF 취약점을 가지고 있는 strcpy함수를 수행하고 printf함수를 수행한다.
프로그램의 스택 상황은 이러할 것이다.
[그림5] main의 스택 상황
Level12와 비슷한데, 이번에는 RET을 system함수가 아닌 execve함수로 보내야 한다. gdb를 통해 execve의 주소 값을 획득하자.
[그림6] gdb를 통해 execve함수의 주소를 획득
giant에서 execve의 주소를 계산한 것처럼 계산도 해보자.
/usr/bin/ldd의 인자로 들어가는 /home/giant/assassin은 권한 문제로 접근할 수 없으니 현재 디렉토리에 있는 /home/bugbear/giant를 집어넣자. 공유 라이브러리이기 때문에 값에는 차이가 없을 것이다.
[그림7] main에서 lib_addr에 들어가는 값 수행
/usr/bin/nm /lib/libc.so.6 | /bin/grep __execve | /bin/awk '{print $1}' 도 수행해보자.
[그림8] main에서 execve_offset에 들어가는 값 수행
두 값을 더해보자.
0x40018000 + 0x00091d48 = 0x400a9d48
gdb를 통해 획득한 execve의 값과 동일하다!
execve를 실행하기 위하기까지의 공격 코드는 다음과 같다.
./giant `python -c'print"A"*44+"\x48\x9d\x0a\x40"'`
그런데 이 코드를 수행하면 You must use execve!가 뜨지 말아야 하는데 아직도 뜬다.
[그림9] execve의 값을 바르게 계산하여 넣었는데도 예상한 결과가 나오지 않음
python에서 \x0a를 개행문자로 인식하여 생기는 문제라고 한다. 전체를 "로 감싸주면 해결된다고 한다. 감싸주고 수행해 보자.
[그림10] "로 전체를 감싸준 후 실행한 모습
You must use execve!가 뜨지 않는다. 모든 제약사항을 넘기고 strcpy함수와 printf함수가 작동한 것이다. 이제 execve의 인자를 넣어 주면 될 것이다. execve의 인자는 세 가지이다. 첫 번째 인자는 전체 경로 명이고, 두 번째 인자는 인수의 배열이다. 마지막 인자에는 환경 설정인데 일반적으로 NULL을 집어넣는다. 우리가 실행하고 싶은 프로그램은 /bin/sh이므로 첫 번째 인자에는 "/bin/sh"가, 두 번째 인자에는 {"/bin/sh",NULL}, 마지막 인자에는 NULL을 넣어 주면 될 것이다. level13에서 해 주었던 것처럼 프로그램을 작성하여 "/bin/sh"의 주소를 구하자.
[그림11] "/bin/sh"의 주소를 구해주는 프로그램의 소스 코드
shell에 execve의 주소를 집어넣고 그 주소가 "/bin/sh"의 주소와 동일할 때 까지 비교하고 난 뒤, 그 값을 출력한다.
컴파일하고 실행해 보자.
[그림12] "/bin/sh"의 주소를 출력하는 모습
두 번째 인자로는 {"/bin/sh", NULL}을 집어 넣어야 하는데 어떻게 하면 될까? gdb로 스택의 끝부분을 살펴보았다.
[그림13] gdb로 스택의 끝부분 조회
0xbfffffe4에 파일경로가 있고 그 다음으로 NULL이 있는 것을 알 수 있다. 우리가 이 프로그램에 전해주어야 할 인자는 {"/bin/sh", NULL}인데 "/bin/sh"문자열이 저기에 들어가면 다음에는 NULL이 옮으로 {"/bin/sh", NULL}처럼 전달 될 수 있을 것이다. 그럼 "/bin/sh"의 문자열이 저기에 들어가지만 실행하는 것은 똑같이 어떻게 해줄 수 있을까? 그러기 위해서는 심볼릭 링크에 대한 이해가 필요하다. 심볼릭 링크는 간단히 말해서 바로가기 같은 기능을 한다. 하지만 이 심볼릭 링크의 이름을 "/bin/sh"의 주소로 전달하면 파일경로가 들어갈 자리에 "/bin/sh"가 들어갈 것이고 다음 자리에는 NULL이 들어갈 것이다. 심볼릭 링크는 ln -s <심볼릭링크를 걸 파일이름> <지정할 이름> 으로 걸어줄 수 있다.
[그림14] 심볼릭 링크를 걸어주는 모습
링크를 걸어주고 아까 그 곳의 스택의 상태를 살펴보자
[그림15] 심볼릭 링크를 걸어준 후의 스택의 모습
그런데 "/bin/sh"의 앞 경로는 필요가 없다. 프로그램 경로의 시작 주소에서 앞에 있는 길이만큼 더해주자.
[그림16] "bin/sh"로 심볼릭 링크를 걸어준 곳의 주소.
NULL은 그 다음에 있으므로 0xbffffffc를 적어주면 될 것이다.
그렇다면 최종 공격 코드는
./<심볼릭 링크의 이름> "`python -c'print"<더미값>"*44+"<"/bin/sh"의 주소>"+"<더미값>"*4+"<"/bin/sh"의 주소>"+"<{"/bin/sh", NULL}의 시작주소>"+"<NULL 주소>"'`"가 될 것이다.
./`python -c'print "\xf9\xbf\x0f\x40"'` "`python -c'print"A"*44+"\x48\x9d\x0a\x40"+"AAAA"+\xf9\xbf\x0f\x40"+"\xf7\xff\xff\xbf"+"\xfc\xff\xff\xbf"'`"가 될 것이다.
공격해보자.
[그림17] 최종 공격 코드
[그림18] 쉘이 작동한 모습
쉘이 작동하였다!
id를 통해 권한을 확인해보자.
[그림19] id 명령어로 권한 조회
권한이 상승한 것을 볼 수 있다.
my-pass로 다음 단계의 패스워드를 획득하자.
[그림20] 다음 단계의 패스워드 조회
'정보보안 > Lord of BOF' 카테고리의 다른 글
[Lord of BOF] Level 13 (darkknight -> bugbear) (0) | 2015.02.10 |
---|---|
[Lord of BOF] Level 12 (golem -> darkknight) (0) | 2015.02.10 |