Level11
$프로그램이름 인자값 명령을 주면 실행 동안 잠깐 uid가 3092로 바뀌고 인자값을 str 배열에 복사해 그 문자열을 출력해 준다. 이때 strcpy 함수가 복사하는 값의 길이를 검증하지 않음을 이용해 버퍼 오버플로우 공격을 일으킬 수 있다. 이를 위해 우리가 알아야 할 것은,
- str 버퍼와 ret 시작 주소값의 거리차
- ret 시작 주소에 넣어줄 쉘코드
1. $cp attackme ./tmp 명령으로 파일을 다른 디렉토리로 복사해주고 gdb로 attackme 파일을 열어 main을 덤프하면 아래와 같다.
눈여겨 봐야 할 곳은 strcpy 함수를 호출하기 전, str 버퍼를 할당시키는 <main+41> 부분이다. 소스코드에서 설정한 str의 버퍼는 256byte인데 ebp-264의 주소부터 입력받는다는 것을 보아 8byte의 더미가 포함되어 있는 것을 알 수 있다. 메모리 구조는 아래와 같으며 총 272byte가 되겠다.
str[256] + dummy[8] + sfp[4] + ret[4]
2. 아래는 쉘을 실행시키는 25byte의 코드이다.
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80
ret을 제외한 268byte에서 쉘코드의 길이인 25byte를 뺴면 243byte가 된다. 이제 제외시켰던 ret 주소를 구해보자. 일단 strcpy를 하고 난 후인 <main+53>에 break를 걸고 dummy[243]+쉘코드[25]+임의의리턴[4] 문자열을 출력해보자.
(gdb) b *main+53
(gdb) r `python -c 'print "\x90"*243+"A"*25+"B"*4'`
이후 x/268x $esp 명령으로 메모리구조를 보면 메모리 어디엔가 0x90909090으로 가득 찬 화면이 뜬다. 이때 0x90909090이 시작하는 주소를 확인한 후 gdb를 나가고 level11 디렉토리로 이동하여 attackme 프로그램을 실행해 버퍼 오버플로우를 일으키면 level12 uid를 얻을 수 있다. 이후 my-pass 명령으로 패스워드를 확인할 수 있다.
[level11@ftz level11]$ ./attackme r `python -c 'print "\x90"*243+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"+"당신의 RET주소를 리틀엔디안 방식으로 작성"'`
level12의 비밀번호는 it is like this입니다.
level12
이번 소스코드는 level11과 다르게 argv[1]은 받지 않는다. 대신, 문자열의 길이를 제한하지 않는 gets 함수로 입력을 받기 때문에 이를 이용해 버퍼 오버플로우를 일으킬 수 있다. 우리는 gets 함수에서 입력을 받을 때 입력받는 주소와 ret 주소의 거리를 알아내고 ret 주소에 쉘을 실행시키는 코드를 작성할 것이다.
level11과 똑같이 str[256]+dummy[8]+sfp[4]+ret[4]의 구조를 갖고 있다. 그렇다면 268byte만큼 더미를 채우고 그 뒤에 실행시킬 주소를 입력하면 되겠지만 ftz 서버에는 버퍼 오버플로우 공격을 막기 위해 aslr 기법을 사용했기 때문에 우리는 환경변수에 쉘코드를 등록할 것이다.
NOP*243과 쉘코드로 이루어진 gets라는 이름의 환경 변수를 만들어주었다.
환경변수의 주소를 반환하는 소스코드를 작성하고 컴파일해 실행하면 아래와 같이 리턴 주소값을 확인할 수 있다.
다시 tmp 디렉토리를 빠져나와 아래와 같이 명령하면 level13의 uid를 획득하는 데 성공한다.
level13의 비밀번호는 have no clue입니다.
level13
argv[1]의 내용을 buf로 복사하며, i가 0x1234567이 아닐 때 버퍼 오버플로우가 일어남을 감지해 종료시킨다. 따라서 i값을 변화시키지 않고 쉘코드를 삽입해야 한다.
- <main+54>를 보면 buf와 ebp 사이의 거리가 1048임을 알 수 있다. buf와 ret의 거리는 4byte를 더한 1052byte일 것이다.
- <main+69>를 보면 i와 ebp 사이의 거리가 12임을 알 수 있다. i와 ret의 거리는 4byte를 더한 16byte일 것이다.
따라서 buf와 i의 거리는 1036byte이고, i의 끝에서 ret의 거리는 16byte에서 long형 4byte를 뺀 12byte일 것이다. 이제 SHELL이라는 이름의 환경변수를 만들어주고 쉘코드의 주소를 아래와 같이 알아내었다.
공격코드의 구조는 다음과 같다.
buf와 i의 거리 | 0x1234567 | 변수 끝과 ret의 거리 | 쉘코드의 주소 |
level14의 비밀번호는 what that nigga want?입니다.
level14
fgets 함수로 buf[20]에 45byte만큼 입력을 받고 check가 0xdeadbeef와 같다면 uid를 올려주고 쉘을 실행시킨다.
우리는 fgets 함수에 입력할 때 buf[20] 뒤 어딘가에 있을 check에 0xdeadbeef를 넣어주어야 한다.
- <main+17>에서 ebp-56이 buf의 주소일 것임을 유추할 수 있다.
- <main+29>에서 ebp-16에 들어있는 값과 0xdeadbeef를 비교한다. ebp-16에 들어있는 값이 check일 것.
따라서 buf의 첫 주소와 check의 첫 주소 사이의 거리는 40byte이다. 그렇다면 입력을 할 때 40byte만큼은 NOP을 채워주고 그 뒤에 0xdeadbeef를 넣어 익스플로잇 코드를 작성할 수 있다.
level15의 비밀번호는 guess what입니다.
level15
level14와 코드가 유사하나 check 변수가 포인터로 변경되었다. 포인터 변수는 변수 값이 아닌 메모리 주소를 저장하는 변수이다. 따라서 *check에 들어가는 주소의 메모리 주소에 0xdeadbeef를 넣어주어야 한다.
buf의 시작 주소는 [ebp-56]이고 *check의 시작 주소는 [ebp-16]이므로 buf와 check 사이의 거리는 40byte이다. 이제 *check에 들어가는 값 0xdeadbeef의 주소를 알아보자. <main+32>의 메모리 주소인 0x80484b0에 들어있는 값을 확인해보고 메모리주소를 1씩 증가시켜보면 0xdeadbeef가 0x80484b2라는 주소에 들어있는 것을 볼 수 있다.
이제 tmp 디렉토리를 빠져나와 공격코드를 작성해주면 된다.
level16의 비밀번호는 about to cause mass입니다.
'3. Pwnable (포너블) > 2) 개념 정리' 카테고리의 다른 글
[2021.11.13] OverTheWire 11-20, 달고나문서 -p.17 (0) | 2021.11.14 |
---|---|
[2021.11.07] OverTheWire 0-10, 달고나문서 -p.12 (0) | 2021.11.06 |
[2021.09.18] 해커스쿨 F.T.Z Level9, 10 풀이 및 Level11 풀이 방향 (0) | 2021.09.19 |
[2021. 09. 11] 해커스쿨 F.T.Z를 통한 기초 명령어 실전 활용 (0) | 2021.09.12 |
[2021. 09. 04] 해커스쿨 F.T.Z를 통한 리눅스 기초 명령어 습득 (0) | 2021.09.04 |