본문 바로가기
Systemhacking/Base info

IO_buf_end attack

by reindeer002 2023. 7. 19.
728x90

IO_buf란?

파일스트림은 buffering이라는 기술을 구현해 더 안정적이고 효율적으로 동작하게 된다.
즉, 데이터의 생산자와 소비자가 서로 처리속도가 다를 경우 예를 들어, 데이터 처리 속도가 [생산자 > 소비자]인 상황에서
완충 역할을 하게 된다.

SysCall은 커널 단에서 작업을 수행하기 위해 존재한다고 말했다,
(Application 단에서 디바이스를 조작할 수 있으면, 공격자의 칩입이나 사용자의 실수에 너무 취약하기 때문임)

즉, 어떤 프로세스가 있다면 이 프로세스가 사용하는 데이터는 User ↔ Kernel 영역을 돌아가면서 실행하게 된다.
(이를 context switching 이라고 부름)

이때 context엔 레지스터 값이나 flag와 같은 정보들이 저장된다.
만약 아래의 코드에서 Hello world를 한 글자마다 context switching을 수행한다고 생각하면, 무조건 손해라는 것을 알 수 있을 것이다.

#include <stdio.h>

char buf[0x400];

int main()
{
    setvbuf(stdout, buf, 0, 0x400);
    //if computer can deal with only one character for each switching, what happened?
    printf("Hello World!\n"); 
    getchar();
}

이해를 돕기 위한 setvbuf 함수의 설명 man setvbuf 명령어로 확인할 수 있음, 무엇을 기준으로 버퍼의 데이터를 처리할지에 대한 옵션

 

그럼 buffer을 사용하여 Hello world를 담아 한번에 처리하면 어떨까?!
이 기술이 File Stream(대표적으로 stdout, stdin, stderr)에 그대로 나타난다.

#include <stdio.h>

char buf[0x400];

int main()
{
    setvbuf(stdout, buf, 2, 0x400);
    printf("Hello World!\n"); 
    getchar();
}

setvbuf(stdout, buf, 2, 0x400); 으로 코드를 변경한 후 stdout을 보면 특정 buffering 영역을 가르키고,
그것은 shortbuf(file stream 내부에 존재함)라는 곳을 가르킨다.

가장 마지막으로 출력한 \n이 들어있는 것을 확인할 수 있음

이때 프로그램의 실행 결과는 출력을 하지 않는 것으로 보인다.
그 이유는 H를 하나 처리하고 다시 user 화면으로 돌아갔기 때문이다.
만약 enter를 한번 입력하면 context switching을 여러 번 진행하여 모든 문자열이 출력될 것이다.

첫 줄에는 아무 것도 출력되지 않았음

 

곧 진행할 실험에서 A * x 만큼 입력하고 stdin의 buffer을 보면 A가 잔뜩 찍혀있고,
이는 stdin에 의해 stdin buffer에 데이터가 써짐을 의미한다.
이제 실험을 위해 scanf("%s", buf);, setvbuf(stdout, buf, 1, 0x400);으로 바꾸자.

#include <stdio.h>

char buf[0x400];

int main()
{
    setvbuf(stdout, buf, 1, 0x400);
    printf("Hello World!\n"); 
    scanf("%s", buf);
}

입력값을 다수의 A 문자열로 줌
BSS 영역에 존재하는(vmmap 명령어로 확인 가능) buf에 AAA.. 가 입력되어 있음

 

그럼 setvbuf(stdin, 0, 2, 0); 을 추가하면 어떨까?

#include <stdio.h>

char buf[0x400];

int main()
{
    setvbuf(stdout, buf, 1, 0x400);
    setvbuf(stdin, 0, 2, 0);
    printf("Hello World!\n"); 
    scanf("%s", buf);
}

위 코드를 빌드한 후 stdin의 IO_buf_end 위치에 데이터를 [원래 IO_buf_end + 0x100] 으로 덮은 값으로 변경해보자.

set {char**}(addr) = (data) 명령어를 통해 메모리 값을 변경할 수 있음
stdin의 내부 buf에 데이터가 삽입된 모습을 확인할 수 있음

만약 stdin의 IO_buf_end 위치에 데이터를 [원래 IO_buf_end + 0x100] 으로 덮은 후 다시 A * x 만큼 입력하면?
터짐 → 그 다음에 관계없는 영역까지 침범해버리기 때문이다.
이를 피하기 위해 보통 뒤의 데이터들은 기존에 적힌 데이터를 그대로 입력해주는 것이 좋다.

즉, 정리하자면 아래와 같다.

  1. 파일 스트림(stdin, stdout, stderr ... )은 bufferinf 기술을 사용한다.
  2. IO_buf_base부터 IO_buf_end가 buffer의 쓸 수 있는 공간이다.
  3. _IONBF 상태일 때, (2) 1 바이트짜리 buffer가 파일 스트림 내부에 만들어진다.
  4. IO_buf_end를 조작한 후 stdin의 파일스트림 구조체를 조질 수 있다^^

이제 마구잡이로 쓸 수 있게 되었고, 심지어 stdin의 IO_buf_end를 unsorted bin attack을 통해 unsorted bin의 위치까지 덮을 수 있게 된다면,
(우연찮게도 stdin에서 unsorted bin까지의 거리가 멀지 않음) wow! 프로세스 내에서 엄청난 자유도를 얻게 된다!

stdin과 main_arena가 굉장히 근접한 위치에 존재함

그러나...

우리가 hook을 덮을 수 있는 상황이 아니라면?
(버그 패치 이슈나 seccomp 등에 의해 막힌다면?)

다음엔 FSOP에 대해 설명해보겠다.

728x90

'Systemhacking > Base info' 카테고리의 다른 글

File Stream Oriented Programming  (0) 2023.07.19
malloc consolidate with Fastbin_Dup Consolidate  (1) 2023.06.04
small bin attack  (0) 2023.06.04
unsorted bin attack  (0) 2023.06.02
unsafe Unlink in heap  (0) 2022.11.07

댓글