본문 바로가기

Storehouse/Diary

[Grotesque] lokihardt (2021.03.28 클리어)

오랜만에 주말에 깔짝 문제 풀 시간이 생겼다.

사실 2주 전에 한번 짬내서 풀어봤다. 그 때는 ubuntu 18.04 환경에서 문제를 분석했는데.. heap chunk 할당방식이 다른가.. 도저히 취약점을 못찾겠더라.

오늘은 lokihardt의 문제서버 환경과 동일하게 ubuntu 16.04로 바꾸어 분석해봤는데.. 취약점이 바로 보이네?

 

취약점이 바로 보이니까 난 이게 진짜 쉬운 문제라고 생각했는데.. 후. ㅋㅋㅋㅋㅋㅋㅋㅋㅋ

 

처음에는 UAF 일으켜서 Heapspray로 힙 영역을 싹 덮어버리고 type confusion 일으켜서 arraybuffer에 fakeOBJ 생성하고.. 

got_overwrite를 통해 free(theOBJ)를 system("/bin/sh")로 실행하려고 했다.

gdb에선 개잘된다. 진짜로. ㅋㅋㅋㅋㅋㅋㅋㅋㅋ

근데 자꾸 서버에서 풀려고 하니까 안되더라? 왜 안되냐 진짜로 왜!! (맞는데 왜안되지?? 진짜 왜안되지?) .. 하고 삽질하다가

lokihardt.c의 맨 위 한줄을 읽어봤다.

 

 

gcc -pie -Wl, -z,now

 

음. 그러니까 바이너리엔

1. pie 보호기법 걸려있어서 code 영역의 주소가 랜덤으로 바뀐다. --> 내가 이용하려 했던 주소를 offset을 이용해 계산하여 우회해야함. 그러니까.. 뭔말이냐면

이런식으로 고정으로 주소를 때려박아서 못쓴다는 말.

다른 방법으로 우회하여 공격 코드를 작성해야한다.

 

2. full RELRO가 걸려있어서 got_overwrite 안된다. 

 

개쉽네 하면서 풀다가 보호기법에 한번 식겁하고는.. 오늘은 그만 풀기로 했다. 다음주에 또 짬나면 깔짝이면서 풀어봐야지.

 

pie야 우회하면 되는데, got_overwrite 대신 다른 방법으로 어떻게 공격해야할지 고민된다.

return address를 바꿀만한 곳이 안 보이던데. 스택주소를 알 수 있는 방법도 없는 것 같고.. 으음.

문제를 더 보면 보이려나..

 

 

=== 2021.03.27

흠.. 거의 다 된 것 같은데 어디서 자꾸 에러가 나네.

문제 특성상 100초라는 시간 안에 쉘을 따야해서, 제한된 heapspray를 쓸 수밖에 없는데.. 그래서 내가 짠 익스코드가 확률적으로 동작한다.

(혹시 높은 확률로 동작하는 익스코드가 있다면 조언좀..?)

이 특성으로 인해 거의 80~90초에 한번 익스한 결과를 확인할 수 있다보니 공격코드 다듬는 데에 쓸데없이 시간이 많이 들어간다.

(낮은 확률 * 80~90초 = 에러확인하는 데에 매우 많은 시간이 걸림)

 

gdb.attach() 라는 기능을 이용해봐야하나 싶기도 하고.. 다 귀찮다 그냥 ㅡㅡ;

 

저녁에 또 시간되면 이어서 풀어봐야겠다.

 

 

=== 2021.03.28

내가 기존에 이용한 방법은 힙에 수백만 바이트를 뿌려서 Heap Grooming을 하고,  Alloc 주소를 예측해서 Type Confusion을 일으키고 exploit을 하는 것이었다.

 

로컬에서는 되게 잘 되는 것 같다. 공격이 약 90초 정도 걸린다. (문제의 시간제한은 100초이길래, 이 의도로 문제를 낸 줄 알았다. 아마 맞을 것 같긴 한데.. 내가 서버에 데이터를 빠르게 뿌리는 방법을 모르겠다.)

근데 막상 서버에다가 수백만 바이트를 때려넣으려니까 너무 오래걸리더라.

 

사실 수백만 바이트 Heap Spray는 상관없는데, 문제가 되는 부분은 그거다. 

Alloc()의 "arrayBuffer[idx] = theOBJ"을 이용해 0x300000 범위에 read string이 담긴 alloc 주소를 때려넣는 게 너무 오래걸린다는거.

로컬에서처럼만 되면 문제가 없는데.. Alloc(), Delete() 함수를 각각 65000번씩 실행하니까 서버는 너무 느리더라.

 

내가 이용한 기존의 방법은 HeapSpray를 약 0x800000 바이트정도 힙에 뿌리고, 이 사이에 alloc을 넣어 alloc의 주소를 대략적으로 유추하도록 코드를 작성했다. Alloc() 함수의 "arrayBuffer[idx] = theOBJ"를 이용해, 약 0x300000 범위의 힙에 readstring이 담긴 2번째 alloc 함수주소를 뿌렸다. 첫번째 UAF alloc의 p->type 주소를 바꾸기 위해..

 

결과적으로는 내가 수행한 방법은 로컬에서는 잘 되지만 서버는 너무 느려서 잘 안된다.

그래서 다른 방법을 찾아봤다.

 

내가 위에서 수백만 바이트를 뿌린 이유는 결국 g_buf나 p->type 주소를 얻어와서 pie를 우회하기 위함이니,

어찌됬든 OBJ 주소를 얻어오기 위한 다른 방법을 수행해야 했다.

 

그 방법은 너무 단순하다.

바로 OBJ의 구조상의 취약점 + UAF를 이용해 Alloc의 p->type 영역을 g_buf 주소로 덮는 것.

이 공격은 정말 확률적으로 일어난다. 기존의 내가 짠 것도 확률적이라 할 말은 없는데;

 

여튼 이런식으로 g_buf를 덮어서 요래요래 조래조래 한 뒤 pie를 우회한다.

pie만 우회하면 공격은 쉽다.

 

이 문제에서 두번째로 주의해야할 보호기법은 full RELRO이다. 이건 got_overwrite까지 못하도록 메모리를 보호한다.

내가 기존에 하려고 했던 방법은 free(theOBJ) -> system("/bin/sh") 였다. free의 got를 system으로 덮는 것.

근데 full RELRO 때문에 막혔다. 인터넷에 뒤적거리다보니 이런 방법이 있더라.

 * __free_hook에 메모리 주소를 걸어놓으면(hang) got_overwrite같은 동작이 일어난다고..

gdb에서 p &__free_hook 를 입력하면 __free_hook 주소가 나오는데 거기에 system 주소 입력해서 한번 실험해보라. full RELRO 걸려있어도 system 함수가 잘 실행되더라.

 

 

문제는 재밌었다. 원래 풀려던 방법인.. Heap Grooming을 하고 UAF가 일어날 것 같은 곳을 예상해서 데이터를 덮은 뒤, fakeOBJ를 이용해 공격을 하는 것도 생각나면 짜봐야지. exploit time이 수십초, 수백초가 걸릴 수도 있다는 걸 배웠다.

 

문제풀이는... 흠. 언제 써야하는데. 지금은 좀 지쳤다.

 

'Storehouse > Diary' 카테고리의 다른 글

와 어떡하지  (0) 2021.01.25
좋은 소식이 들려오네요  (0) 2020.12.31
책 집필을 슬슬 준비하고 있습니다.  (2) 2020.12.21
[Grotesque] maze 클리어  (0) 2020.10.11
[Grotesque] aeg (2020.10.03 클리어)  (0) 2020.10.01