5회차 목표 : 정보보안 공부
5회차 결과 : Format String Bug 악용 공격
버퍼 오버플로우보다 간결하게 사용할 수 있다.
FSB: Format String Bug
일반적인 테크닉 중에 복잡할 수 있는 스택 기반 공격이 ROP이다. 스택에 데이터 넣고,
리턴 가젯 찾아서 엮기. 그런 공격의 경우 버퍼 오버플로우로 쉘코드 집어넣고 그랬지만,
스택에 실행 권한이 있을 때만 가능하다. 요즘에는 안 된다. 코드 삽입은 어렵고, 기존에
메모리에 올라와 있는 API 코드 등을 이용. 필요할 때 사용. 실제로 많이 사용하는게
ROP이다. 결국에는 가젯(코드 조각)을 찾아서 원하는 대로 레지스터 세팅해서 로직을 짜
면 된다. 굉장히 복잡한 구현이다. 보통은 부분부분해서 함수 실행을 위해 체이닝 걸어서
쓰는 것이다. 이런 식으로 공격. 힙 영역 쪽의 공격은 다루지 않지만 컨셉은 비슷하고,
공격 시 어떤 식으로 동작하는지 파악하는 게 중요하다. malloc일 경우 분석해서 메모리
할당 및 프리 분석해서 기반을 쌓아서 공격하는 것이다. 시스템에 대한 이해 분석이 중
요하다. 공격 기법 정리해둔 사이트도 많고, 새롭게 우회해 공격할 때. 이것들도 모두 경
험이 중요하다. 실제로 다루는 내용들을 지금 현업에서 정보보호 직무를 하면서 대학원
을 온다. 근데 모르는 내용들이 많다. 버그 헌팅이나 시스템 공격, 악성코드 분석하는 사
람들도 잘 모르는 사람 많다. 해킹, 취약점 찾아서 공격하는 코드와 관련된 내용이다. 정
보보호 한다고 해서 소프트웨어 및 시스템 공격 공부를 반드시 하지 않아도 된다.
포렌식의 경우는 데이터 잘 뽑아내는 스크립트 작성. 파일 확인해서 로그 확인하는 정도.
해킹에 가까운 것은 아이폰 비밀번호 뚫기. 여러 번 입력할 수 있게 하는 기술이 이스라
엘에 있다. 셀레브레이트라는 회사. 우리나라에서 많이 쓴다. 이전 버전의 아이폰에서 가
능. 무한대로 입력 가능. 패스워드 길이 및 조합을 변경하면 몇 백 년 걸릴 수도 있다.
비밀번호 확인이 50ms 대기 시간이 있다.
이론은 ROP를 다뤘으나 실습은 어렵지 않다. 오늘은 지난 번 것보다 쉽다. printf 관련
내용.
페이지 3 variadic function이 우리가 원하는 만큼 패러미터를 넣을 수 있다. printf도 이런
가변함수에 속한다. printf 원리를 보여주려고 만든 myprint이다. 물론 실제 printf도 이러
한 매크로를 사용하고 있다. 스택에 데이터 저장하고, 포인터 이동하면서 스택에 저장된
데이터 꺼내오는 일을 한다. va
list는 매크로로 정의됐으나 포인터이다. 스택의 메모리를
_
가리키는데 그걸 처음에 세팅해주는 초기값 세팅을 위한 것. Narg를 기준으로 ap값을 세
팅해준다. 그 주소로부터 몇 바이트 가져오느냐를 실행하는 역할이다. int 형이면 4바이
트, 뭐 이런 식으로. 각 루프에서 두 개씩 int, double 파싱해서 출력. printf 이렇게 구현
되어 있다.페이지 4 narg를 기준으로 해서 스택의 메모리 위치 초기값을 세팅해준다. va
_
해서 포인터가 이동한다. 뭐 이런식으로 구현이 되어 있다. 페이지 참고.
arg를 이용
페이지 5 va
_
arg를 이용할 것이고, format string 어떤 입력을 받을지 포맷을 세팅. 포맷을
어떻게 하느냐에 따라 뒤따라오는 파라미터의 개수가 달라질 것이다. 가변하게 만들 수
있다. %는 포맷 specifier이다. 타입 크기만큼 스택에 저장되어 있는 걸 가져올 것이고 프
린트 될 것이다. va_Arg va
list를 사용한다.
_
페이지 6 내부적으로 어떻게 동작하느냐. 어떻게 스택이 되느냐. 함수 호출 시의 스택 구
현과 같다. 필요 파라미터가 하나씩 저장되고 리턴 어드레스 저장되고.... 좌측의 포맷 스
트링에 맞춰 인자가 저장된다. 포맷 스트링의 주소도 저장되어 있다. 호출될 때 인자가
이렇게 저장이 된다.
페이지 7 문제는 포맷 스트링 버그. 이 버그는 어떤 것이냐. 포맷스트링에서 정한 것과
인자가 다를 때 발생. 포맷 인자가 3개인데 입력은 2개 줬다. 데이터를 꺼내서 꺼내올
때, 입력은 스택의 임의의 값이 프린트가 된다. 입력은 안 했는데 스택에 어쨌든 그 위치
에 있는 값을 갖고 올 것이다. 컴파일러에서는 워닝으로 줄 수도 있다. 포맷스트링에서
포맷은 3개인데 매칭은 2개이니 워닝. 물론 컴파일은 된다.
페이지 9 포맷 스트링 정의. printf 구성을 보면, 인자들이 매핑됨. 컴파일러는 optional
argument를 안 넣어도 워닝만 할 뿐이다.
페이지 10 2019가 프린트 된다. 없으면 스택에 있는 어떤 값을 갖고와서 프린트 된다.
페이지 11 어떤 타입의 파라미터가 프린트 되는지. 파라미터는 스택에 푸쉬된다. 레퍼런
스나 값이 될 수 있다.
페이지 12 포맷 스트링에서 들어갈 수 있는 파라미터들이 여러 개 있다. 이건 뭐 알면
됨. %n은 잘 안 쓰는데, 이런 것도 있다. 공격하는데 중요한 역할을 하는 포맷이다. 뭔가
를 write하니 좋다. arbitary read/write가 가능. 임의적 읽기 쓰기.
페이지 13 문제 되는 상황. 인풋 값을 유저에게 받는다. 헬로라고 넣어질 때, 헬로가 나
오는데 입력을 %x라고 하면? 스택에 있는 4바이트 16진수 값이 프린트 된다.
페이지 14 앞에서 했던 내용. 주소, 값, 등등이 들어갈 수 있다. 포맷을 참조해서 %나올
때마다 , VAlist가 가리키는 곳에서 하나씩 꺼낸다. (타입의 크기만큼.)
페이지 15 뭐 가능하다.
페이지 16 100바이트 저장, 4바이트 저장. 주소 찍어주고, 값 찍어주고, 인풋을 받아서 넣
어주고. 위험한 코드가 된다. 유저로부터의 인풋을 받았는데 포맷 스트링을 넣으면 스택
에 있는 값을 읽어버릴 수 있다. 취약한 부분.페이지 17 va
_
list이동하고, printf가 호출되고, 스택 내용을 보면 프레임에는 포맷 스트링
주소가 있고, 로컬 변수가 스택에 저장되어 있고 그럴 거다. 이런 코드가 있을 때 어떤
공격을 할지 케이스를 보자.
페이지 18 다섯가지. 1번 프로그램 터치기. 크래쉬.
페이지 19 %s를 실행하니 스택에 있는 값을 문자열의 주소로 생각해서 뭘 가져오려고
할 것이다. 8개만큼 있으니 가져오려는데 그 주소가 유효한 주소라면 뭔가 프린트 되는
데 그렇지 않을 경우 매핑이 안 된 경우에는 프로그램이 크래쉬 된다.
페이지 20 스택 값 출력. %x %d 등 이런 값으로 인식해서 4바이트 씩 가져오는데 크래
쉬는 안 나고, 읽어서 프린트하는 결과를 낼 수 있다. 정보를 얻어 낼 수 있다. 비밀번호
나 그런 게 저장되어 있는 상태라면? 읽다보면 원하는 값을 내올 수 있겠다. 어쨌든 가
져올 때, 현재 스택 포인터를 기준으로 이동해서 가져오기 때문에 스택의 특정 변수 값
을 갖고 오자 하면, 미리 예측(프로그램 어셈블리 단위로 분석) 혹은 보통은 트라이얼 에
러로 가져온다. 삽질 많이 해서 얻어오기 가능.
페이지 21 3번째. 이전까지는 임의의 메모리를 읽을 수 있는 권한을 얻은 거였다. 이제는
write 할 수도 있다. 포맷에 %n을 쓰면 된다. i라는 변수의 값이 5로 변경된다. %n 직전
까지 프린트 된 1바이트 캐릭터의 개수 5가 해당 &i 주소로 들어가서 값을 변경한다. 그
래서, 스택에 있고 포인터도 있고, read write도 가능하게 된 거다. 주소에 캐릭터의 개수
를 write 할 수 있다.
페이지 22 입력을 넣을 수 있는데, 시행착오, gdb로 얻어내면서 가정한다. 프로그램을 컴
파일해서 gdb로 열고, 해당 변수의 주소를 보면 bffff304라는 주소를 파악. 그러면 인풋
값이 세팅되어 실행이 되며 변수의 값이 변경된다. 페이로드 스크립트에 대한 설명이다.
페이지 23 이런 식으로 포맷 스트링이 들어가 있을 것. 정확하게 뭔가를 아는 건 아니
고, 분석해서 %x의 개수 위치를 정해서 이런 포맷을 꾸며야 겠다. 포맷 스트링 참조해서
파싱하고 스택 포인터 이동하고 4바이트씩 읽어올 것이고 스택 포인터가 움직일 것이
다. %n을 만났을 때, 지금까지 프린트된 캐릭터 개수를 쓰니 현재 스택포인터가 가리키
는 주소에 그 값을 넣는다.%x의 개수를 잘 꾸며서 포인터가 0xbffff304를 가리키니 해당
위치에 라이트 될 것이다. var라는 값이 업데이트가 될 것이다. %x가 실행될 때마다 이동
한다.
%n을 만났을 때 현재 스택포인터가 가리키는 곳에 저장한다.페이지 24 인풋 만든거고, 입력을 넣은 것이다. 입력이 인풋에 있는 값으로 들어가게 될
것이다. 들어가서 결과가 들어간다. 마지막에 var를 프린트하는 애가 0x2c로 변경된 결과
를 보여줄 수 있다. 44개 캐릭터를 읽고 해당 값이 저장된 것이다. 결국엔 스택의 임의
변수 값들을 조작할 수 있는 상황이다.
페이지 25 아까 %x 대신 %8x 혹은 천 만 x를 쓰면 프린트할 때 빈 공간이 있으면 0으
로 채워라 그렇게 되는데, 프린트되는 개수를 세팅할 수 있고, 원하는 임의의 값을 쓸 수
있다. 트릭이다. 이런 방식으로 하면 계산 시 0x9896a9라는 16진수로 표현된 수가 저장
될 것이다. 리턴 어드레스 조작을 할 수 있다. 리턴 어드레스의 주소를 파악해서 포맷스
트링 버그로 write할 수 있는 것이다. 0이 천 만 개 프린트 되는 동안 시간이 오래 걸린
다. 1바이트를 천 만개 나 찍기는 걸리니 빠르게 하는 팁이 아래부터다.
페이지 26 빠르게 공격할 수 있는 팁.%hn은 2바이트 단위로 쓰기 %hhn은 1바이트 단위
로 쓰는 것. 일부만 변경할 거면 4바이트 전체 바꿀 필요는 없으니까. 프린트하는 개수가
좀 줄어들 수 있다.
페이지 27 0x66887799를 변경하고자 한다. n만을 사용하면 오래 걸리니 hn으로 하프로
잘라서 변경하면 훨씬 더 빠르게 할 수 있다. 0xbffff304부터 2바이트씩 반은 6688넣고
반은 7799를 넣어서 빠르게 공격을 수행할 수 있다. 리틀엔디안 방식이니 낮은 주소 높
은 주소 해서 넣기.
페이지 28 메모리를 반 씩 쓰는 테크닉. %hn을 이용한 것. 아래 페이지 보고 다시 이해
하자.
페이지 29 포맷 스트링 설명이다. 스택 포인터 이동으로 6688찍고, 두번 째 거 찍어주고,
4368이 16진수이니 10진수 하면 1111이니 더하면 그리 된다. 처음 파싱되어서 쓰이고,
첫 번째 라이트 하고 중간에 프린트를 처리하기 위해서 4바이트 더미값을 @@@@를 넣
은 것이다. 첫 번째 hn은 hn이 파싱이 될 때, 포맷 스트링의 어드레스가 있고, 해당 포맷
스트링이 저장 되어 있을 거다. 마지막 hn은 bffff304에 넣어지는 것이다.
페이지 30 설명이다. @이 있는 이유는 중간 %x 처리하기 위해서 더미캐릭터 4바이트 넣
은 것이다.
페이지 31 마지막 malicious code 리턴 어드레스를 조작하는데, 리턴 어드레스 저장된
위치 파악해서 앞선 코드를 집어 넣으면 된다. 챌린지 처음 거는 스택에 실행권한이 없
어서 안 될 것이긴 한데, 라이브러리 시작 주소다 라고 생각해보자. 라이브러리 주소 알
아내서 리턴어드레스 저장된 주소 찾아서 A를 B로 쓰게 하면 된다.
페이지 32 주소 변경. 뭐 쉘코드 주소가 있겠고, 거기에 리턴 주소를 바꾸면 될 것이다.2바이트 씩 bfff f358 해서 넣기. 2개로 나눈 다음에 작은 값 큰 값 해서 공격코드 작성.
페이지 33 메모리 구조 보면 리턴 어드레스가 38c에 있으니 말리셔스 코드는 358에 있
으니 밑에서부터 올라오면서 포맷 스트링 작성해서 원하는 값을 만들어 내면 된다.
페이지 34 버퍼 오버플로우가 먼저 발견, 뒤에 발견 되었으니 위험하다는 걸 인식. 발견
하기 너무 쉬운 FSB 그래서 이를 악용한 사례가 적다. 찾기 쉬워서 테크닉이고 자시고가
별로 없다. 잘 안 나온다. 버퍼 오버플로우는 스택 영역에 버퍼가 있으면 스택 버퍼 오버
플로우, 힙에 있는 경우 스트럭쳐 만들었을 때, 오브젝트 만들고 그 안에 버퍼가 있으니
힙 버퍼 오버플로우. 최근까지 나오기도 한다. 옆에 있는 객체 값을 변경하든지 그런 행
위 가능. OOP를 보면 변수 있고, 메소드 있는데, 각 객체가 사용하는 메소드의 테이블이
있고, 이를 조작하면 이상한 함수 실행 가능.
첫 시간의 버퍼오버플로우만 이해하면 그 이후로는 복잡한 공격 기법 이용 연습하면 해
커 가능. 방어기법 적용 됐을 때 어떻게 우회할 것인가. full-chain이라고 완전한 루팅이
버그를 하나만 이용해서는 안 된다. 엔지니어링이 복잡하니 가격이 비싸다. 그런 걸 하려
면 분석이 중요하다. CS 기본 개념을 잘 이해하는 게 중요하다.
페이지 35 포맷 스트링이 counter measure가 뭐가 있는가. 유저 인풋을 포맷스트링으로
하는 걸 피해라. 이상한 입력을 집어넣기 때문이다. 유저 인풋과 조합해서 만드는 걸 빼
자. 포맷은 하드코드로 세팅하고 사용하도록 하자. 유저 인풋을 파싱하게 두지 말자.
페이지 36 컴파일러가 워닝을 띄운다. 1번 케이스에 대해서 워닝을 준다. 2번 케이스는
워닝이 없다.
페이지 37 clang도 그렇고 gcc도 그렇다.
페이지 38 컴파일러에게 옵션을 줘서 더 강력하게 워닝을 주게 한다. 주의하도록 한다.
잠재적인 문제가 발생할 수 있다.
" 아규먼트 타입으로 와서 확인이 안 되었다. 개발자
확인해봐라.
" 포맷이라는 변수까지 찾아들어서 체크까지는 안 된다. 포맷이 인자로 들어
오니 위험할 수 있다고 알려준다.
페이지 39 마지막으로 개발자가 주의하는 거 말고 방어기법있나? 포맷스트링 버그 방지
에 유용한 것인가.
Address randomization 기술은 ROP에 대한 방어기법. 이미 메모리에 올라와 있는 코드
조각들을 엮는 것이다. 메모리 주소 어디에 있느냐 찾아야 하는데, 어드레스 랜더미제이
션이 켜져 있으면 매번 코드가 실행될 때마다 메모리 주소가 달라져있다. 매번. ASLR 깨는 방법. 정보 유출 인포메이션 리퀴지. 런타임에 공격을 하는데 코드의 시작 위치가 변
경되니 LIBC 라이브러리가 0x1000에 로딩이 되었다. 공격자는 이 주소를 모른다. 런타임
에서 printf가 0x1100에 로딩이 된 걸 알 수 있는 버그가 있다면, 공격대상 시스템과 동
일한 환경에서 컴파일 해보면, 오프셋은 동일하니 때려 맞추기 가능. 이를 이용해서 정보
유출(information leakage) 활용. 실제 적용은 라이브러리 시작 위치가 로딩될 때 랜덤하
게 위치하게 되는데, 이를 좀 더 조밀하게 해서 함수단위로 섞든지 주기적으로 런타임
때 한다든지. 학술적 연구를 한다. 실제로 적용되긴 어렵다. 실제 프로그램은 어렵다. 테
이블도 업데이트 해야하고 그러니까..
스택 힙 실행을 못하게 하는 건 소용 없다. 실행 권한 있는 쪽으로 가면 되니까 ROP 이
용하면 된다. 리턴 주소 바꿔서 실행 권한 있는 코드로 뛰어 가면 된다.
스택 가드. 버퍼 오버플로우 막는 건데. 스택 메모리 구조에 버퍼가 있고 리턴 어드레스
가 있다고 치면 랜덤한 값을 넣어주고, 다시 리턴할 때 랜덤하게 설정한 그 값이 달라져
있다면? 막아준다. 물론, 소용없다. 버퍼오버플로우같은 경우 linear 하게 하는 방식으로
중간 랜덤값이 변경되면 확인 가능한데, 포맷 스트링은 그 중간의 것 안 건들고 가기 때
문에 소용이 없다.
'[활동 정리] - 비밀번호 : helloㅁㅁㅁ > [2024]동계 모각코 개인' 카테고리의 다른 글
[모각코] 최종 회고 (0) | 2025.02.13 |
---|---|
[모각코] 동계 모각코 6회차 개인목표 및 결과 (0) | 2025.02.13 |
[모각코] 동계 모각코 4회차 개인목표 및 결과 (0) | 2025.01.21 |
[모각코] 동계 모각코 3회차 개인목표 및 결과 (0) | 2025.01.21 |
[모각코] 동계 모각코 2회차 개인목표 및 결과 (0) | 2025.01.10 |