value[1] = __readfsqword(0x28u); setvbuf(stdin, 0LL, 2, 0LL); setvbuf(stderr, 0LL, 2, 0LL); setvbuf(stdout, 0LL, 2, 0LL); puts("Please, tell me your name: "); fgets(your_name, 12, stdin); your_name[strcspn(your_name, "\n")] = 0; printf("Hi, "); printf(your_name); // fsb puts(" give me a soul you want to send to hell: "); __isoc99_scanf("%lu", value); getchar(); puts("and in which circle you want to put him/her: "); __isoc99_scanf("%lu", &addr); getchar(); *addr = value[0]; puts("Done, bye!"); return0; }
your_name 변수는 bss 영역에 존재한다. 글자수를 11자까지만 적을 수 있기 때문에 (null 포함하면 12자) 주소를 leak하는 데에 쓸 수 있다. 그리고 아래와 같이 임의쓰기가 존재한다.
원래 ld_leak + 0x13b0 주소에는 x 값이 저장되어 있다. 그리고 x + 8에 위치함 값(주소)이 call이 된다.
ld_leak + 0x13b0 주소를 your_name으로 덮었기 때문에 최종적으로 your_name + 8에 위치한 값(주소)이 call이 된다.
이는 우리의 입력값이니 원하는 주소를 call할 수 있다.
이를 위해 your_name에 onegadget을, your_name+8에는 your_name 주소를 넣어야 한다.
사실 최종적으로 실행되는 주소는 pie_base + y 형태로 계산되어 call이 되고, y는 x + 8, 즉 your_name + 8 에 위치한 값이다.
결국 your_name에는 p64(one_gadget) + p16(your_name_off) + p8(0)이 들어가야 한다.
이 방법은 main 함수가 종료되어 return될 때 _dl_fini 함수에서 .fini_array를 참조 후 호출하는데, .fini_array을 어떻게 참조하는지에 집중하면 이런 익스플로잇을 작성할 수 있다. your_name 변수를 .fini_array로 착각하게 만들었다.