일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- packet filter
- epoll_wait
- wrefresh
- Compiler
- edge trigger
- NAPT
- ncurses
- iptables
- LOB
- architecture
- epoll
- .nret core 배포
- 풀이
- Docker
- ioctl
- vtable
- 어셈블리어
- rfc5508
- BOF
- packet flow
- DOCKER-USER
- cbpf
- wnourefresh
- .net core 7
- 취약점
- doupdate
- level trigger
- mvwin
- C언어
- REDIS
- Today
- Total
Tuuna Computer Science
Dynamic Linker Advancture 1탄 본문
추가) JMPREL 이라고 .rel.plt부분의 구조체는각 각 0x24의크기인데이는 reloc_offset으로 _dl_fixup()함수내부에서 찾아낸다.
그리고 rel.plt 구조체 내에는 3개의 멤버가 있는데 각 각, reloc_offset에 맞게 정의된 함수의 GOT주소값
r_info라고 하위 1바이트는 심볼의 타입을 나타내고 나머지는 값은 dynsym내에서 인덱스값을 지정하는거 같다.
즉, 처음 .rel.plt의 주소값이 A라면 특정 함수의 relocation 구조체의 주소값은 A+reloc_offset*24로 설명되며
특정 함수의 .dynsym 위치는 .dynsym의 주소값 + relocation.r_info(나머지 부분)*24하면 구해진다.
그럼 이 특정 함수의 .dynstr의 값은 어케구하냐 dynsym에는 Elf64_Sym->st_name 이라는 값이 있는데 이부분을 파싱해서
.dynstr의 주소값에 이 부분을 더해주면 문자열이 나온다.
대략 5시간의 디버깅으로 정리한것을 작성함.
64bit ELF에 대한 모험은 없길래 공부할겸 정리요.
(분석 나름 열심히 했는데 ㅎ 어렵네)
-
Dynamic Linker 모험
해당 파일은 dynamically linked로 되어 있으며 not stripped으로 심볼이 살아있다.
0x400561 부분에 puts함수를 호출하기 위해 puts@plt가 적혀있다.
이 puts@plt를 까보면
JMP [rip+0x200be2] #got.plt
push 0x0 #reloc_offset Elf64_Rela 구조체의 오프셋 (구조체 배열의 인덱스)
jmp 0x400420 이 적혀있다. #?
좀 더 자세히 말하자면 0x601018자체는 GOT의 주소가 아니다 단지 PLT이후에 함수의 실제 주소가 적혀있는 주소다.
GOT의 실제주소는 함수의 실제 주소가 있는 부분 0x601018에서 0x18을 빼준 0x601000이다.
GOT자체는 포인터 배열로써 정보를 가지는데
GOT[0] : .dynamic의 시작 주소
GOT[1] : link_map 연결 리스트의 첫번째 노드의 시작주소
GOT[2] : dl_runtime_resolve()의 시작주소
GOT[3] : PLT에서 참조하는 GOT영역( 함수의 실제 주소가 들어가 있는 부분)
GOT[4] : PLT에서 참조하는 GOT 영역2
GOT[5] : //
.
.
.
이런식으로 쭉 형성된다.
잡지식, relocation table은
typedef struct
{
Elf64_Addr r_offset; //재배치 후(함수 바인딩)실제 함수 주소가 저장될 GOT영역의 주소
Elf64_Xword r_info; //첫 1바이트는 재배치 타입, 나머지는 심볼 테이블 정보
Elf64_Sxword r_addend;
}
처음 [rip+0x200be2]에 있는 주소로 점프하는것 같다. 확인해보자.
rip+0x200be2에는 0x400436이 들어있다.
이는 puts@plt+6의 rip값 이다.
그 이유는 puts라는 함수가 처음 호출되었을 때는 got.plt에 함수의 실제주소값이 들어있지 않고
특수한 여러 함수를 거친 뒤에 GOT에 값이 들어가게 된다.
일단 계속 진행하자.
그럼 puts@plt+11 주소에 있는 0x400420으로 jump를 해보자.
0x400420으로 점프하니 [rip+0x200be2](0x601008)값을 push하고 [rip+0x200be4](0x601010)으로 점프한다.
이때 0x601008에 있는 값은 link_map이라는 구조체이다.
Struct link_map
{
Elfw(Addr) l_addr; //Base address shared object is loaded
char* l_name; //Absolute file path object was found in
Elfw(Dyn)* l_ld; //Dynamic section of the shared object
struct link_map* l_next; //Chain of loaded objects.
struct link_map* l_prev;
}
link_map은 ld_loader가 참조하는 링크지도로써 라이브러리의 정보를 담고 있다.
l_name은 문자열 포인터, STRTAB(.dynstr)에 있는 라이브러리 전체 경로 문자열을 참조
0x601008에는 0x7ffff7ffe170이라는 값이 들어있다.
그리고 jump를 하는 0x601010에 들어있는 값은 0x7ffff7dec750으로
_dl_runtime_resolve_xsavec()함수의 주소값으로 JMP한다.
함수 내부에 _dl_fixup함수가 호출됨을 볼 수 있다.
함수 호출까지 single step을 하고 함수로 넘어가는 인자를 살펴봤습니다.
rdi에는 x07ffff7ffef170 이라는 값이 들어가 있는데 이는 좀 전에 push [0x601008]했던 값입니다.
즉, link_map 구조체 포인터이다.
rsi에는 0x0 이 인자로 넘어갑니다. 이때 rsi에는 우리가 맨 처음 push 0x0 즉, relc_offset을 push 했던 것이다.
그럼 dl_fixup함수(0x7ffff7de4df0) 내부를 조금 훑어보면
처음 rdi에는 link_map 구조체 포인터가 있는데 여기서 0x68을 더해 .dynamic 섹션의 주소값을 구한다.
0x68이 뭔뜻인지 모르겠는데 아마 구조체 내에 멤머접근을 위한 오프셋인가?
암튼 dl_fixup+14에서의 rax에는 0x600ea0이 들어간다. 만약 dynamic 주소값이들어 간다면 0x600e20일텐데 왜 0x600ea0이 들어가는지는 잘 모르겠다.
0x600ea0 주소를 잘 살펴보면
뭔가 TAG:Address 형식으로 볼 수 있다.
이에대한 설명은 사진으로…
암튼, 이렇게 해서 tag 5번에 대응하는 d_ptr은 0x400330이다. 이를 까보면
문자열이 나온다.
문자열이 적혀있는 STRTAB주소다.
.dymstr이다.
즉, .got.plt의 주소에 0x601000에 있는 .dynamic 섹션의 주소값을 확인해서 tag와 ptr을 찾아가면 된다.
좀 더 디버깅해보자.
궁금한것이 생겼다. 만약 내가 디버깅하는 것이 read함수라면?
여기서 dl_fixup+75의 주소에 있는 rbx값이 뭘로 바뀔까 rdx와 rcx가 가리키는것은 무엇일까
밑에 메모리에서 내가 디버깅하는 함수가 read와 puts가 아니라면 0x601018이후에 각 함수의 GOT의 주소값이 추가될까. 만약 추가된다면 어디에서 추가되는 것일까. 파일을 복사하고 새로 디버깅해보자.
scanf()함수를 디버깅했을 때 rbx값이 예상되로 바꾸이었고 push 부분도 0이 아닌 2로 reloc_offset도 바뀌었다.
위 메모리사진을 잘보면 0x601000, 즉 GOT[0]은 항상 .dynamic 섹션의 주소를 가리키고 다음은 link_map, dl_runtime_resolve 순으로 가리키는데 함수의 실제 주소가 들어가는 GOT[3]부분 부터는 puts와 read의 실제 주소가 들어있고 scanf는 아직 들어가지 않았다. 그 이유는 아직 scanf를 디버깅하고 있고 puts와 read는 이미 했기떄문에 들어간것이다.
또한, rdx는 함수 번호인거 같다.
또, 여기서 의문이 들었다. 만약 함수 호출 순서를 바꾼다면 push되는 값은 바뀔까
실험결과 push되는 값과 함수번호는 바뀌지 않는다.
하지만, 상당히 이상한 점이 있다.
함수의 호출 순서는 바꼈지만 .plt.got내에 기록되는 순서는 바뀌지 않았다는 점인데
아마 reloc_offset을 이나 고유 번호등을 생각해서 그런거 같다.
하지만 아직, RCX는 무얼 의미하는지는 아직 미지수다. 디버깅 노하우상 scanf()함수의 경우 RCX값이 0x500000007이였고 puts는 0x100000007이여서 앞자리는 함수번호인듯한데 뒷자리는 아직 모름.
이쯤에서 잠시 넘기고 디버깅을 마자해보자.
중요한것은 어떻게 rdi에 원하는 문자열을 찾고 넣었냐는 것이다. 놓쳤다… 다시 디버깅…
여기서 RSI가 0x4002d0을 가리키는데 이는 .dynsym 섹션을 의미한다.
dynsym에 접근해서 어떤 메타이터를 esi에 넣은다음 문자열 주소에 offset을 더한다.
여기서 그럼 dynsym 구조체를 파악하면 될듯하다.
dl_lookup_symbol_x()함수이다.
인자로는 일단 호출하려는 puts함수의 문자열을 rdi에 넣고 있으며
rsi에는 link_map 구조체 포인터를 넣고 있다. 이렇게해서 특정함수의 실제주소를 GOT내에 쓴다.
다음 같은 함수를 호출할때는 이런 개짓거리를 하지 않고 바로 호출한다.
실제 GOT 영역에 주소가 저장되는 과정
dl_runtime_resolve() → dl_fixup() → dl_lookup_symbol_x() → do_lookup_x()
reloc_offset : 구조체의 시작주소를 얻기 위해 이용
64bit ,RELA(.rel.plt) 영역에서 해당 함수 Elf64_Rela 구조체의 오프셋 (구조체 배열의 인덱스)
라이브러리 주소를 구하는 과정
함수이름의 시작 주소를 인자로 dl_lookup_symbol_x()함수를 호출
함수 이름을 해쉬값으로 바꿔서 do_lookup_x()함수에서 검사
실제 라이브러리 영역에서의 심볼테이블 인덱스를 구함(do_lookup_x 함수에서 반환)
인덱스를 가지고 라이브러리 상에서의 오프셋을 구함.
로드된 라이브러리 파일에서 해당 함수의 Elf64_Sym 구조체 주소에 접근
libc의 .dynsym 영역 + symidx offset = libc에서 찾고하는 함수의 Elf64_Sym 구조체 영역
libc_base address + Elf64_Sym→st_value = 실제주소
돌발 생각, 공격 포인트
만약에 0x600e20이 .dynamic 섹션이라고 했잖아 이떄 동적 로더가 .dynamic 섹션을 파싱해서
d_tag에 맞게 d_ptr을 구한다고 했는데 d_tag 5에 해당하는 부분을 fastbin dup 공격으로 malloc할당한 다음에 bss영역의 주소를 준다면?
bss영역에는 각 함수의 offset에 맞게 공격함수를 대치시켜놓는다면? Offset 부분은 dynsym을 잘 살펴보면 될거 같고, 만약 이방법이 된다면 unlink를 사용하지 않아도 될텐데?
libc_base address leak을 어떻게 해야하지?
'system hacking' 카테고리의 다른 글
gdb 정리글 & libc source code debugging (0) | 2020.01.03 |
---|---|
[malloc] overflow로 인접한 chunk의 heap 주소 구하기 (0) | 2019.07.04 |
[malloc] malloc & heap 두들이기 3탄 unsortedbin library base address leak (0) | 2019.07.04 |
[malloc] malloc & heap 찍어누르기 2탄(fastbin dup into stack) (0) | 2019.07.03 |
dlmalloc 조지기 1탄 (5) | 2019.05.28 |