bugbear의 소스를 확인하는 것으로 시작하겠습니다.

스택이 배신을 했다고 나오네요. 시작주소가 메모리의 시작주소가 \xbf인 곳은 사용 불가능합니다.

그럼 메모리의 공유 라이브러리 쪽의 함수를 사용하는 RTL기법을 사용하면 될듯 합니다.

RTL기법은 LOB gate를 풀 때 보다 자세히 적어 놓았습니다.

http://grayfieldbox.tistory.com/entry/LOBLord-Of-BufferOverflow-gate-gremlin

 

system(“/bin/sh”);라는 명령어를 스택에 구축하도록 하겠습니다.

만들어주어야 하는 스택의 모습은 다음과 같습니다.

“/bin/sh”’s address

junk(4byte);

system()’s address

SFP

buffer(44byte)

 

우선 system()함수가 들어있는 주소를 알아내도록 하겠습니다.

0x40058ae0system()이 존재하네요.

 

이제 /bin/sh의 문자열을 메모리에 넣고 주소값을 얻어내겠습니다.

/bin/sh가 들어있는 주소는 0x400fbff9입니다.

 

페이로드는 다음과 같이 됩니다.

“A”*44 + “\xe0\x8a\x05\x40” + “\x90”*4 +”\xf9\xbf\x0f\x40”

예상대로 쉘이 획득 되었습니다.


이제 원본 파일을 대상으로 진행하겠습니다.

성공적으로 bugbear의 쉘이 획득 되었습니다.

bugbear의 패스워드는 new divide입니다.

darkknight의 소스를 우선 확인하겠습니다.

코드가 확 짧아졌네요.

problem_child함수가 argv[1]을 받아와 버퍼에 입력 하는데 이상한점은 버퍼가 40byte인데 41byte를 입력하네요.

값을 입력할 때 1byte가 초가되어 SFP의 제일 마지막 1byte를 덮어 씌울것으로 보입니다.

 

이러한 취약점을 Frame Pointer Overflow라고 합니다.

 

스택에서 SFP위 주소에는 RET가 존재합니다.

SFP가 가르치는 메모리의 +4byte에 있는 값은 다음 명령어의 실행 주소가 됩니다.

 

이번 문제는 이를 이용하는 문제입니다.

problem_child에서 SFP의 마지막 1자리를 우리가 원하는 대로 변조할수 있다는건 EBP를 원하는데로 설정 할 수 있다는 뜻입니다.

만약 41번째에 0xa0을 입력하여 problem_childSFP0xbfffffa0이 들어가게 되면, problem_child함수가 끝나고 ebp0xbfffffa0가 되고, main함수가 끝날때 ebp+4의 값이 eip로 들어가니 0xbfffffa4의 값이 eip에 들어가게 됩니다.

 

그럼 문제는 해결 된듯 합니다.

problem_childebp만 구할수 있다면, 원하는 값을 다 넣을수 있죠.

 

gdb를 통해 problem_child ebp값을 알아보겠습니다.

problem_childebp값은 0xbffffabc입니다.


ebp-40부터 40byte데이터를 확인해 보도록 하겠습니다.

0xbffffab4부터 40byte A가 들어가있고 41번째에 B가 들어가 sfp0xbffffa42로 바꾸었습니다.

 

페이로드를 생각을 해보면 sfp0xbffffab0으로 수정하고 버퍼에는 eip + NOP슬라이드 + 쉘코드를 넣으면 될듯 합니다.

0xbffffab8(4byte) + NOP슬라이드(11byte) + 쉘코드(25byte) + \xb0

 

예상한대로 값을 넣도록 하겠습니다.

세그펄트가 떳네요.

 

core dump로 원인을 확인해 보겠습니다.

스택의 영역이 조금 밀렸네요.


SFP0xbffffac0으로 수정해주고 eip값을 0xbffffac8로 변경해 주도록 하겠습니다.

성공적으로 쉘이 획득되었습니다.

 

원본 파일을 대상으로 공격하도록 하겠습니다.

성공적으로 darkknight의 쉘이 획득되었습니다.

darkknight의 패스워드는 new attacker이네요.

Gloem의 소스를 먼저 보겠습니다.

코드에 변경점이 좀 있네요.

Egghunter가 사라졌으니 환경변수를 입력 가능합니다.

그리고 프로그램 종료 전에 buffer부터 0xbffffffff까지 ret주소가 들어있는부분을 제외하고는 전부다 0으로 초기화 하네요.

즉 매개변수, 버퍼, 환경변수 다 사용 불가능하다는 얘기 같습니다.

 

그럼 저희가 쉘코드를 넣을수 있는 위치는 buffer 아랫부분에 있는 메모리 영역입니다.

Code, data, bss영역은 코드 컴파일시에 결정이 나고, heap영역은 동적할당으로 잡히는 영역이니 프로그램 실행시에 저희가 임의로 값을 넣어줄 수는 없습니다.

 

그럼 할수 있는 방법이 무엇이 있을 까요?

 

여기서 잠시 생각해 봐야 하는 것은 프로그램이 동작할때 메모리에 실행한 프로그램만 로드하는 것이 아니라는 것입니다. 프로그램 내부에서 사용하는 함수들도 실행을 해야하니 전부 메모리에 로드를 해야곘죠.

 

일반적으로 내부 함수들은 외부 라이브러리에 존재하기때문에 다른 메모리 영역에 존재합니다.

프로그램에서 사용하고 있는 memset이나 strcpy등 다른 함수들은 다 메모리 중 저 위치에 로드가 되어 있는 것입니다.

그래서 실행시에 저기서 가져오게 되는 것이죠.

 

그럼 여기에 등록이 되어 있지 않은 다른 임의 함수를 추가해서 사용할 때는 어떻게 될까요?

메모리 상에 로드를 해야 실행을 할 수 있을테니 프로그램 동작시에 사전에 메모리에 로드하게 될것입니다.

이러기 위한 환경변수가 리눅스에 존재 합니다.

바로 LD_PRELOAD이죠. LD_PRELOAD 환경변수에 파일을 등록 하면 프로그램이 메모리에 로드 되기 전에 환경변수에 등록한 파일을 먼저 메모리에 로드하게 됩니다.

 

그리고 이전 문제에서의 기억을 되짚어 보면 프로그램의 해당 메모리 영역의 끝부분에는 해당 프로그램의 이름이 올라가 있습니다.

 

, 만약 LD_PRELOAD 환경변수에 AA라는 이름의 파일을 등록하고 프로그램을 실행하면 프로그램의 스택 아래쪽에는 AA라는 이름의 파일의 코드가 등록이 되고 그 파일의 이름 또한 스택에 존재하겠죠.

 

코드상에서 0으로 초기화 하는것은 buffer부터 0xbfffffff까지 이므로 LD_PRELOAD로 메모리에 로드한 영역은 초기화 되지 않습니다.

 

이 방법을 이용해 이번 문제를 풀면 됩니다.

 

우선 아무런 동작을 하지 않는 파일을 하나 만들어 쉘코드 이름으로 컴파일 해주도록 하곘습니다.

shared object를 컴파일 하기 위한 명령어는 gcc –fPIC –shared입니다.

 

이제 환경변수 LD_PRELOAD에 이 파일을 등록해 주면 됩니다.

정상적으로 쉘코드를 이름으로 하고 있는 파일이 LD_PRELOAD에 등록이 되었네요.

 

이제 gdb를 통해 이 이름의 위치를 확인하도록 하겠습니다.

golem의 메모리 영역보다 아래쪽에 있을테니 esp-3000부터 확인하도록 하겠습니다.



NOP 슬라이드와 쉘코드가 메모리에 적재되어 있는것이 보이네요. Ret 주소에 대략 0xbffff5a0쯤 넣어주면 될것 같습니다.

 

쉘이 떳네요.

 

이제 원본 파일을 대상으로 공격하도록 하겠습니다.

golem의 쉘을 획득 하였네요.

golem의 패스워드는 cup of coffee라고 합니다.

Skeleton의 소스코드를 한번 확인해 보도록 하겠습니다.



소스코드에 걸려 있는 조건을 확인해 보도록 하겠습니다.

1.    Argv갯수 제한 없음

2.    환경변수 사용 불가능

3.    argv[1][47] == ‘\xbf’

4.    argv[1]의 길이는 48byte

5.    버퍼 사용 불가능

6.    모든 매개변수 사용 불가능

 

모든 매개변수를 초기화 하는 코드가 들어간것이  매우 타격이 크네요.

매개변수도 불가능하고, 환경변수도 사용 불가능하고, 버퍼도 사용 불가능하고, RTL도 불가능하고, 그럼 무엇을 할 수 있을까요?

 

일단 gdb로 프로그램이 끝나기 바로전에 bp를 걸어 모든 메모리를 확인해 보겠습니다.


더 이상 확인 할 수 없을 때 까지 일단 확인해 보도록 하겠습니다.

! 빙고! 메모리의 끝부분에 파일의 경로가 들어가 있는것을 발견했습니다.

그럼 문제가 다 해결됫네요.

쉘코드로 이름이 되어 있는 링크파일을 작성하고 ret주소를 변경해 주면 될것 같습니다.

 

우선 이름이 쉘코드로 된 링크파일을 생성해주겠습니다.

 

파일이 생성이되었으니 이제 gdb로 파일 경로가 들어있는 메모리의 주소를 얻어 내도록 하겠습니다.

0xbfffff51부터 의도한 파일경로가 들어가 있네요.

 

ret주소를 0xbfffff80쯤으로 잡고 실행하도록 하겠습니다.

한번에 쉘이 얻어졌네요.

 

원본 파일을 대상으로 진행하도록 하겠습니다.

skeleton의 쉘을 획득 하였습니다.

Skeleton의 패스워드는 shellcoder이네요.

vampire의 소스코드를 한번 보겠습니다.


.. 소스코드가 매우 짧아졌네요.


모든 제약이 다 풀렸습니다. argv[2]에도 넣을수도 있고 argv[1]의 길이도 상관 없고, 환경변수도 상관없고, 버퍼도 상관없네요.


다만 한가지 제약이 새로 추가되었습니다.

Argv[1][46]\xff면 안되네요.


지금까지 넣었던 메모리의 주소를 생각하면 항상 \xbfff로 시작했었습니다.

\xbffeffff보다 작거나 같은 메모리 위치에 있는 값을 써야 한다는게 핵심이네요.


스택은 구조상 높은 주소로부터 낮은 주소로 자라게 됩니다. 즉 매개변수에 전달되는 값의 크기가 크면 클수록 그만큼 스택은 아래쪽에 자리 잡게 된다는 말입니다.

저희가 사용할 수 있는 ret 주소는 0xbffeffff 이므로 0xbfffffff에서 부터 0x10000만큼 내려 와야 합니다. 0x1000010진수로 65536입니다.


한번 크기가 65536인 입력을 준 뒤 ebp를 확인해 보도록 하겠습니다.

예상대로 ebp의 주소값이 0xbffeffff보다 내려갔습니다.

65536개의 NOP슬라이드를 넣어주고 쉘코드 그 뒤에 넣어준 다음, ret 주소를 적당한 위치로 바꿔주면 될 듯 합니다.

 

ret주소를 0xbffeffff로 하여 입력해 보도록 하겠습니다.

바로 쉘이 떴네요.

NOP슬라이드의 개수가 많다보니 적당히 넣은 값에도 쉘이 떳습니다.

 

이제 원본 파일에 실행하도록 하겠습니다.

성공적으로 vampire의 쉘을 획득 하였네요.

Vampire의 패스워드는 music world입니다.

Troll의 소스를 확인하도록 하겠습니다.



첫번째로 보이는 차이점은 11번째부터 15번째라인까지의 내용이 변경 되었네요.

입력 할 수 있는 argument의 갯수가 제한이 없던 것에서 1개로 바뀌었습니다.

대신 argv[0]의 길이 제한은 사라졌네요.

여전히 버퍼나, 환경변수나, argv[1]0으로 초기화 하는 것으로 보아서 링크 파일을 이용해서 argv[0]에 쉘코드를 입력해야 할 것 같습니다.

 

그럼 파일의 이름을 쉘코드로 하는 링크파일을 생성해 주겠습니다.

어라.. 파일이 생성이 안되네요그 이유는 바로 쉘코드에 포함되어 있는 \x2f때문입니다. \x2f“/”인데요. 따라서 경로가 한번 나눠지게 되고 정상적으로 링크가 생성되지 않는 것입니다.

이번 문제의 핵심은 이 \x2f를 포함하고 있지 않은 쉘코드를 이용하는 것입니다.

 

이러한 쉘코드를 흔히 다형성 쉘코드라고 부릅니다.

다형성 쉘코드란 쉘코드를 암호화하고, 사용하기 전에 복호화를 원본 쉘코드를 다시 구하여 동작하는 쉘코드를 말합니다.

더미다같은 패킹과 비슷한 개념이라 생각하면 될것 같습니다.

 

제가 사용한 다형성 쉘코드는 다음과 같습니다.

"\xd9\xc5\xd9\x74\x24\xf4\xb8\x15\xc3\x69\xd7\x5d\x29\xc9\xb1\x0b\x31\x45\x1a\x03\x45\x1a\x83\xc5\x04\xe2\xe0\xa9\x62\x8f\x93\x7c\x13\x47\x8e\xe3\x52\x70\xb8\xcc\x17\x17\x38\x7b\xf7\x85\x51\x15\x8e\xa9\xf3\x01\x98\x2d\xf3\xd1\xb6\x4f\x9a\xbf\xe7\xfc\x34\x40\xaf\x51\x4d\xa1\x82\xd6"

 

우선 NOP슬라이드를 포함한 쉘코드를 이름으로 하는 링크를 생성하도록 하겠습니다.

NOP슬라이드는 많아서 나쁠거 없으니 한 100개쯤

 

그리고 적당히 ret값을 넣어 오류를 유도하도록 하겠습니다.


 

에러가 났으니 보다 구체적으로 쉘코드의 위치를 얻기 위해 core dump 디버깅을 하도록 하겠습니다.


NOP슬라이드가 보이네요. 얼추 0xbfffff50쯤을 ret 주소로 하면 될듯 합니다.


빙고~! 쉘이 떳네요.

 

이제 원본 파일을 대상으로 공격하도록 하겠습니다.

정상적으로 troll의 쉘을 획득하였습니다.

패스워드는 aspirin이네요..

이 문제 풀려고 3일동안 삽질했더니 아스피린이 간절하긴 합니다

뭔가 센스있는 패스워드이네요 ㅎ

orge의 소스를 확인하도록 하겠습니다.

darkelf의 소스코드에 비하여 한부분이 추가되었습니다.



argv[0]의 크기가 77인지 확인하는 코드가 추가 되었습니다.

 

argv[0]은 프로그램을 실행할때 첫번째 인자를 의미 합니다.

$./orge라고 프로그램을 실행하게 되면

argv[0]에는 /home/darkelf/./orge가 들어가게 됩니다.

, 프로그램의 이름이 /home/darkelf를 포함하여 77byte가 되어야 합니다.

그런데 권한이 없기 때문에 우리가 직접 이 프로그램의 이름을 변경할 수는 없습니다.

따라서 심볼릭 링크를 이용하여 다른 파일을 실행해서 orge를 실행해 주면 argv[0]를 마음대로 바꿀수 있습니다.

(심볼릭 링크에 대한 정보는 구글을 이용해 주세요)

 

ln –s orge link명령어를 통해 orge의 링크파일을 생성해 주고, ./link를 실행하면

argv[0]에는 /home/darkelf/./link가 들어가게 됩니다.

 

이방법을 이용하여 이름이 긴 파일을 생성해 주면 됩니다.

총 필요한 argv[0]77byte 이므로 /home/darkelf/14byte를 제외하고 파일의 이름을 63byte로 생성해 주면 됩니다.

 

그리고 페이로드는 이전 문제를 풀던것과 동일하게 argv[2]NOP슬라이드와 쉘코드를 넣는 방식을 이용하면 될듯 합니다.

 

그럼 우선 테스트를 위하여 tmp폴더에 orge를 옮기고 링크파일을 생성하도록 하겠습니다.

링크파일은 경로가 /home/darkelf에 이어 /tmp 4byte가 추가되었기 때문에 파일의 이름은 59byte가 될듯 하네요.

테스트를 해봤더니 정상적으로 조건을 통과하네요.

Argv[0]./가 들어가는 것을 없애기 위하여 절대 경로로 실행하였습니다.

상대경로로 명령어를 실행하실 분은 이름의 길이를 계산할때 ./ 2byte를 고려해주셔야 합니다.

 

예상 페이로드는 다음과 같습니다.


이제 ebp값을 알아내기 위하여 gdb로 디버깅을 하겠습니다.

알아낸 ebp의 위치는 0xbffffa38입니다. 입력 값에 따라서 프로그램이 들어가는 메모리의 위치가 변경될 수 있기 때문에 실행시 입력 페이로드도 동일하게 넣어 주어야 합니다.

 

보다 정확하게 페이로드의 위치를 구하기 위하여 x/40x $ebp 명령어를 통하여 ebp부터의 메모리 내용을 확인하도록 하겠습니다.

확인 결과 ret의 주소를 0xbffffc08쯤으로 하면 될듯 합니다.

 

수정된 페이로드를 넣어 실행하도록 하겠습니다.

역시나 한번에 안되고 에러가 뜨네요.

 

core dump를 이용하여 gdb 디버깅을 통해 쉘코드의 위치를 확인하도록 하겠습니다.

0xbfffffb8 이후로는 쉘코드가 들어갈 공간이 충분하지 않습니다.

 

따라서 다시 ebp부터의 값을 확인해 보도록하겠습니다.

0x90들이 들어 있는부분의 한 지점을 잡아주면 됩니다.

 

페이로드의 Ret0xbffffba0으로 수정하여 다시 실행해 보도록 하겠습니다.

정상적으로 쉘이 획득이 되었네요.

 

복사파일에서 쉘이 획득 되었으므로 이제 원본 파일을 대상으로 진행하면 될듯 합니다.


우선 길이가 63byte인 심볼릭 링크를 생성하고 페이로드를 입력해 보도록 하겠습니다.


성공적으로 orge의 쉘이 획득 되었네요.

orge의 패스워드는 timewalker입니다.

darkelf의 소스코드를 보도록 하겠습니다.


orc에서 wolfman으로 갈 때 buffer hunter가 추가 되었는데, darkelf로 가니깐 argument의 길이를 체크하는 부분이 추가가 되었네요.

 

Argv[1] 뒷부분에 쉘코드를 입력하는 것을 방지 하기 위해서 추가한 코드같은데, orc에서부터 저희가 해왔던 풀이법은 argv[2]에 쉘코드를 입력하는 방법이니 그대로 적용해도 될듯합니다.(페이로드 하나가지고 너무 우려먹네요…)

 

앞선 방법과 마찬가지로 페이로드를 작성해 주면 될듯 합니다.

http://grayfieldbox.tistory.com/entry/LOBLord-Of-BufferOverflow-goblin-orc

에러가 날때 해결법 또한 앞선 방법과 마찬가지로 하면 되구요.

http://grayfieldbox.tistory.com/entry/LOBLord-Of-BufferOverflow-orc-wolfman

darkelf의 비밀번호는 kernel crashed입니다.

wolfman의 소스코드를 한번 보도록 하겠습니다.

orc의 소스코드와 상당히 유사합니다.

차이점이라면 마지막에 buffer hunter라고 해서 memset(buffer, 0, 40)으로 buffer를 초기화해주는 코드가 추가 되었습니다.

buffer hunter가 적용 되어 있으니, buffer에 쉘코드를 넣는 방법은 안될듯 합니다.

그렇다면 orc를 풀었을 때와 마찬가지로 argv[2]에 쉘코드를 넣는 방식을 통하여 문제를 풀도록 하겠습니다.

orc와 동일한 방법으로 ebp를 구하고 페이로드를 작성했습니다.

(goblin->orc 풀이 : http://grayfieldbox.tistory.com/entry/LOBLord-Of-BufferOverflow-goblin-orc)


작성한 페이로드를 입력해 보겠습니다.

ret 주소가 0xbffffbf0이면 될줄 알았는데 뜨질 않았네요.


core dump가 생성 되었으니 core dump를 이용해 문제를 해결하겠습니다.

core dump를 이용해 디버깅을 열었더니 0xbffffc04에 있는 값이 무엇인지 알수 없다고 나오네요.

그럼 0xbffffc04의 값이 무엇인지 확인해 봐야겠죠.

확인결과 ret가 들어있는 부분이었네요. 그 뒤부분으로 NOP 썰매가 들어가 있는 것을 확인 할 수 있습니다. 그럼 ret에 넣어준 값을 NOP이 들어있는 주소로 변경해 주면 될듯 하네요.


Ret0xbffffc14로 바꿔 주도록 하겠습니다.

빙고 쉘이 떳습니다. Setuid가 걸린 wolfman이 아닌 복사한 파일에 해서 wolfman권한이 없을테니 원본 wolfman에 입력하여 비밀번호를 획득하도록 하겠습니다.

정상적으로 wolfman의 쉘을 획득하였습니다. 비밀번호는 love eyuna이네요.

orc의 소스파일을 확인해 보도록 하겠습니다.


egghunter라고 해서 환경 변수 메모리를 초기화 하는 코드가 보이네요.

그리고 첫번째 매개변수의 48번째 데이터가 \bf를 확인하는 코드도 보입니다.

LOBgcc의 버전이 2.91.66이므로 스택에 더미가 생성이 되지 않고 따라서 buffer[40]은 스택에서 40bytes만큼 공간을 차지할 것입니다. 그러면 입력시점부터 ret까지의 거리는 44bytes가 되겠죠.

 

그럼 현재까지 습득한 정보들을 정리해 보도록 하겠습니다.

1.    환경변수를 이용한 쉘코드 입력 불가

2.    첫번째 인자 48번째 데이터는 \xbf

3.    입력시점부터 ret까지 거리는 44bytes

 

이 문제 또한 쉘코드를 입력하고 ret주소를 쉘코드가 들어있는 메모리의 주소로 변경하여 풀면 될듯 합니다.

입력을 해줄수 있는 메모리는 두가지가 있습니다. 첫번째로 40byte크기의 버퍼안에 쉘코드를 입력해 줄 수 있고요, 두번째로 두번째 인자로 쉘코드를 넘겨 줄 수 있습니다.


두번째 방법을 이용해 문제를 풀도록 하겠습니다. 

그렇다면 입력 페이로드는 다음과 같이 되겠네요.

 

./orc `python –c ‘print “A”*44 + “argv[2]주소”’` `python –c ‘print “쉘코드”’`

우선 argv[2]의 주소를 한번 알아보도록 하겠습니다.

 

argv[2]ebp를 기준으로 8byte + strlen(argv[1]) 만큼 떨어져 있습니다.

argv[1]의 입력값이 48byte이므로 ebp+56byte지점이 argv[2]지점이 됩니다. 그럼 ebp값을 구해보도록 하겠습니다.

 

Gdb로 파일을 열어 프롤로그가 지난 지점에 중단점을 걸고 레지스트리를 확인해보겠습니다. 이때 중요한건 입력값에 따라 스택의 메모리 주소가 바뀔수 있기 때문에 최대한 입력값을 동일하게 줘서 실행해야 한다는 것입니다.

 

main+3지점에 중단점을 걸고 페이로드를 넣어 실행하도록 하겠습니다.

ebp의 주소는 0xbffffa68이네요. 그럼 argv[2]의 주소는 0xbffffaa0이라고 예상할 수 있습니다.

 

그럼 페이로드의 주소값을 반영해 입력해 보도록 하겠습니다.

정상적으로 쉘이 떳네요.

만약 쉘이 뜨지 않고 segmentation falut(core)라고 뜨신 분들은 gdb –q orc core명령어를 통해 core 디버깅 해서 ret에 들어갈 주소값을 수정해 주시면 됩니다.

 

이제 원본 파일에 시도해 보도록 하겠습니다.

orc권한의 쉘이 떳네요.

 


orc의 비밀번호는 cantata입니다.

+ Recent posts