0x80에 값을 넣을때 main문에선 read~>strcpy를 통해 값을 넣는다. 여기서 이용할 성질은 read는 입력이 끝난 후 null을 쓰지 않지만 strcpy는 null값을 쓴다. 따라서 만일 read에 0x80바이트를 다 넣을 경우, 마지막 1바이트가 null값으로 추가되고, 이 값은 qword_601160 값의 마지막 60부분에 침범하여 qword_601100으로 바뀌게 된다.
qword_601100은 printf() 의 매개변수로 쓰이기에, FSB방법을 사용할 수 있다.
FSB방법이란,
그냥 입력값에 인자를 넣어서 스택주소를 노출시키는 취약점을 이용한 것이고, 아직 완벽하게 이해하지 못했다.. 내일 fsb에 대해 더 자세하게 글을 올리겟다 :(
생각을 해보면,
처음 y를 입력받는 구문엔 16바이트를 read하고, 이름을 입력받을땐 80바이트를 받는다.
처음 입력받는 단계에서 우리가 가고싶은 주소, 0x601080을 입력하면 0x6010e0과 0x601160사이 어딘가에 주소가 들어갈 것이다. 0x601080엔 우리가 원하는 flag의 db가 들어가있고, 이 부분의 형식자를 %s로 뽑아주면 원하는 flag가 나올것이다.
여기서 원하는 0x601080의 위치를 찾았고, (9번째 %p) 이 부분을 %s로 바꿔주면,
원하는 flag가 나온다.
함수 참고~>
p32(value) : 32비트 리틀 엔디안 방식으로 패킹해주는 함수 (p32(value, endian='big')을 하면 빅 엔디안으로 패킹해준다)
p32(0x12345678) = \x78\x56\x34\x12
p64(value) : 64비트 리틀 엔디안 방식으로 패킹해주는 함수 (p64(value, endian='big')을 하면 빅 엔디안으로 패킹해준다)
if문에서 scanf의 리턴값은 받아온 값의 개수라고 보면 되고, 위의 코드에서는 3개를 모두 받아와야 조건이 충족되는 것이다.
그리고 위 코드상으론 flag를 불러오는 부분이 보이질 않는다. 함수를 뒤져보다가, win함수를 발견하였다.
int win()
{
return system("cat /flag");
}
해당 함수의 주소는 400822이다.
문제에서 코드의 취약점은, "v7[v6] = v4 + v5 ;" 이 문장에 있다.
v7이란 배열은 크기가 11로 정해저 있지만, v6에 대한 제약이 없다.
또한 오늘 처음으로 알게된 사실인데, 다음과 같이 v6의 제약이 없으면 다음 스택을 (sfp, ret)을 배열 v7[12, 13]이 가리키고 있다. 따라서 ret부분에 win함수의 주소를 넣어주면 된다. 또한, while(1)구문을 빠져나오기 위해 문자 3개까지 넣어주면 페이로드는 완성이다.
pwntool로 작성해 보자.
from pwn import *
p = remote("svc.pwnable.xyz", 30002)
p.recv(512)
p.sendline("4196386 0 13")
p.recv(512)
p.sendline("a a a")
print p.recv(512)
변수 v4와 v5의 차이가 4919이어야 하고, 상위 if문은 v4, v5가 각각 4918보다 작거나 같아야 한다고 되어있다.
맨 위로 돌아가서, v4, v5가 선언된 부분을 보면, 정수형으로 선언되어있고 각각 0으로 초기화 되었다가 scanf함수에서
첫 번째로 입력한 정수는 v4로 들어가고, 두 번째로 입력한 정수는 v5로 들어간다.
그러면 페이로드는 첫 번째 입력값에 4918, 두 번째 입력값에 -1이 들어가도록 작성하면 된다.
pwntool로도 풀어보자.
from pwn import *
p = remote("svc.pwnable.xyz", 30001) (remote>> nc 접속 명령어)
print p.recv(11) #프로그램이 출력하는 값을 봐야할때(이땐 11바이트만 받는다)
p.send("4918 -1") #페이로드를 보내는 명령어
p.interactive() #쉘 접속 (bash랑 같은거)