data overwrite by setbuf

C
unsigned __int64 __fastcall main(int a1, char **a2, char **a3)
{
  char v3; // bl
  unsigned __int64 v5; // [rsp+8h] [rbp-10h]

  v5 = __readfsqword(0x28u);
  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  v3 = makw();
  *(_BYTE *)makw() = v3;
  return __readfsqword(0x28u) ^ v5;
}

다음과 같이 문제가 있었을때 보통 setvbuf를 쓰지만 setbuf 도 같은 기능을 한다. 물론 설정이 좀더 약하지만,

자세한 내용은

그런데 이때 저 함수를 사용해서 버퍼를 바꾼다면, file 관련 함수에서 _IO_file_underflow 가 발생할때 내부 버퍼에 많은 양에 데이터를 한번에 읽어 드린다. 이때 버퍼에 많은 양에 데이터를 입력할 수 있다. file 관련 함수가 getchar() 같은 1바이트 입력함수여도 말이다.

따라서 만약 ROP등으로 setbuf 함수 계열의 인자를 설정 할 수 있다면, buf를 스택 또는 Data 영역으로 설정 할 수 있다면, 큰 바이트의 overwrite가 가능하게 된다.

대충 실제 문제의 writeup을 아래에 그대로 적겠다.

Idea

C
Dump of assembler code for function main:
0x00000000004005d3 <+0>:	push   rbx
0x00000000004005d4 <+1>:	xor    esi,esi
0x00000000004005d6 <+3>:	sub    rsp,0x10
0x00000000004005da <+7>:	mov    rdi,QWORD PTR [rip+0x20061f]  # <stdin@@GLIBC_2.2.5>
0x00000000004005e1 <+14>:	mov    rax,QWORD PTR fs:0x28
0x00000000004005ea <+23>:	mov    QWORD PTR [rsp+0x8],rax
0x00000000004005ef <+28>:	xor    eax,eax
0x00000000004005f1 <+30>:	call   0x400560 <setbuf@plt>

만약에 rsi를 원하는 주소로 설정하고, 흐름을 0x4005da로 변경했다고 하자.

그러면, setbuf(stdin,rsi) 를 호출하게 된다.

그러면, rsi가 가리키는 곳을 0x2000만큼 덮을 수 있게 된다.

그러면, rsi를 어떻게 setting하냐?? sleep을 이용하면 된다.

C
unsigned int
__sleep (unsigned int seconds)
{
  int save_errno = errno;
  const unsigned int max
    = (unsigned int) (((unsigned long int) (~((time_t) 0))) >> 1);
  struct timespec ts = { 0, 0 };
  do
    {
      ...
      if (__nanosleep (&ts, &ts) < 0) // here!
        /* We were interrupted.
           Return the number of (whole) seconds we have not yet slept.  */
        return seconds + ts.tv_sec;
    }
  while (seconds > 0);
  __set_errno (save_errno);
  return 0;
}

__nanosleep (&ts, &ts) 이 부분에서, rdi, rsi에 스택 영역의 주소가 들어간다고 한다.

그래서, puts_got를 변조해서 setbuf(stdin,&ts)가 호출되도록 하고, getchar()가 호출되게 되면 스택에 0x2000만큼 적을 수 있게 된다고 한다.

getchar가 한글자만 받는 거 아닌가라는 생각을 할수는 있지만, buffer에 저장된다!!!

게다가 놀랍게도 적기시작하는 주소가 getchar 내부에서 호출하는 __IO_file_underflow 함수의 return 주소라고 한다. ㄷㄷ

즉, 이제 부터는 그냥 rop다.