printf("It's not that easy though, enter 4 numbers to use to guess!\n"); do { // ask user for input printf("1st number: "); scanf("%lu", &a); printf("2nd number: "); scanf("%lu", &b); printf("3rd number: "); scanf("%lu", &c); printf("4th number: "); scanf("%lu", &d);
// perform some calculations on the numbers d = d + c; c = c ^ b; b = b - a;
if (check(d, num_sold)) { printf("Woohoo! That's exactly how many she sold!\n"); printf("Here's a little something Sally wants to give you for your hard work: %lx\n", &d); } else { printf("Sorry, that's not quite right :(\n"); }
// go again? printf("Would you like to guess again? (y/n) "); scanf("%s", &resp);
} while (resp == 'Y' || resp == 'y');
return; }
voidwelcome() { printf("Sally sold some sea SHELLS!\n"); printf("Try to guess exactly how many she sold, I bet you can't!!\n"); }
printf("Would you like to guess again? (y/n) "); scanf("%s", &resp);
그 전에 check(d, num_sold)을 통과하면 d 변수의 주소를 알려준다. 이 주소는 스택 주소이기 때문에 쓸데가 많다. check(d, num_sold)을 통과하기 위해 num_sold = rand();을 예상해야 하는데, 이는 seed가 없기 때문에 항상 일정하다. 처음에 2바이트 브루트포싱을 이용하여 값을 예측하려고 했으나, 계속 실행하고 테스트하다 보니 0x4500으로 일정한 것을 확인했고, 서버도 동일했다.
Solve
rbp+8은 ret이다. 우리는 현재 shellcode를 실행시킬 수 있다. ret 뒤에 shellcode를 위치시켜주자. shellcode가 저장된 주소를 ret에 넣어주자. 우리는 스택 주소를 알고 있기 때문에 가능하다.
참고로 shellcode를 실행시킬 때 스택이 모자랄 수도 있기 때문에 sub rsp, 0x100 이 어셈도 같이 실행시켰다.