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입니다.

+ Recent posts