1. 서론
one gadget, one shot gadget 등으로 불리며, libc 파일 내에서 execve(/bin/sh,0,0) 과 같은 셀을 실행시켜주는 hex code가 나열된 위치를 출력해주는 프로그램이다.
즉, return 시 onegadget 주소로 jmp하면 셀이 실행된다는 것이다.
물론 libc 파일 내에서 직접 하나씩 찾아도 되지만, 노가다를 자동화해줬다는 점에서 큰 도움이 된다.
2. install
한 줄만 치면 된다.
$ gem install one_gadget
필요하면 앞에 sudo 정도 붙여주면 끝.
다만 요구사항으로 ruby 버전이 2.1.0보다 커야 한다.
설치가 안되는 것 같으면 ruby를 설치하거나 ruby --version으로 버전을 확인하자.
3. 사용법
3.1. 기본 사용법
기본적인 사용법은 아래와 같고, 이 정도면 충분하다고 생각한다.
$ one_gadget /lib/x86_64-linux-gnu/libc.so.6
# 0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
# constraints:
# rsp & 0xf == 0
# rcx == NULL
#
# 0x4f322 execve("/bin/sh", rsp+0x40, environ)
# constraints:
# [rsp+0x40] == NULL
#
# 0x10a38c execve("/bin/sh", rsp+0x70, environ)
# constraints:
# [rsp+0x70] == NULL
출력되는 내용들을 보면 아래와 같이 구성되어 있다.
# 0x4f2c5 execve("/bin/sh", rsp+0x40, environ) #offset 값, 함수의 형태
# constraints: #요구 조건
# rsp & 0xf == 0 #rsp & 0xf 값이 0이어야 함.
# rcx == NULL #rcx 값이 NULL이어야 함.
다만, 조건이 여러개인 경우 or 인지 and 인지는 정확히 모르겠다.
어딘가에 이를 테스트한 포스트를 봤는데 기억이 안난다...
3.2. 상세 사용법
하지만 공식 사이트의 메뉴얼에 따라 상세하게 뜯어보자.
$ one_gadget
# Usage: one_gadget <FILE|-b BuildID> [options]
# -b, --build-id BuildID BuildID[sha1] of libc.
# -f, --[no-]force-file Force search gadgets in file instead of build id first.
# -l, --level OUTPUT_LEVEL The output level.
# OneGadget automatically selects gadgets with higher successful probability.
# Increase this level to ask OneGadget show more gadgets it found.
# Default: 0
# -n, --near FUNCTIONS/FILE Order gadgets by their distance to the given functions or to the GOT functions of the given file.
# -r, --[no-]raw Output gadgets offset only, split with one space.
# -s, --script exploit-script Run exploit script with all possible gadgets.
# The script will be run as 'exploit-script $offset'.
# --info BuildID Show version information given BuildID.
# --base BASE_ADDRESS The base address of libc.
# Default: 0
# --version Current gem version.
3.2.1. -b, --build-id
libc의 BuildID[sha1] 값 만으로 onegadget을 구할 수 있다.
$ one_gadget -b aad7dbe330f23ea00ca63daf793b766b51aceb5d
# 0x45526 execve("/bin/sh", rsp+0x30, environ)
# constraints:
# rax == NULL
#
# 0x4557a execve("/bin/sh", rsp+0x30, environ)
# constraints:
# [rsp+0x30] == NULL
#
# 0xf1651 execve("/bin/sh", rsp+0x40, environ)
# constraints:
# [rsp+0x40] == NULL
#
# 0xf24cb execve("/bin/sh", rsp+0x60, environ)
# constraints:
# [rsp+0x60] == NULL
다만, 로컬에서 테스트해보니 해당 값은 없다고 나온다.
┌──(kali㉿kali)-[~/Downloads]
└─$ file /lib/x86_64-linux-gnu/libc.so.6
/lib/x86_64-linux-gnu/libc.so.6: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=a4c98c0c7c7803311fbd918df8fb08db852cef3d, for GNU/Linux 3.2.0, stripped
┌──(kali㉿kali)-[~/Downloads]
└─$ one_gadget -b a4c98c0c7c7803311fbd918df8fb08db852cef3d
[OneGadget] Cannot find BuildID [a4c98c0c7c7803311fbd918df8fb08db852cef3d]
아무래도 모든 libc 파일을 가지고 있지는 않은 것 같다.
3.2.1. -n, --near
만일 got에 값을 쓸 수는 있지만, base address는 알 수 없는 상황이라고 가정해보자.
즉, PIE 보호 기법이 걸려있어 libc base가 매 실행시 변경되며, memory leak이 불가능한 경우이다.
이런 경우에는 got address의 마지막 2 byte만 변조해서 onegadget을 시도해볼 수 있고,
이 방법을 사용하면 1/16 확률로 성공할 수 있다.
예를 들어 exit 함수 또는 mkdir 함수의 got을 덮어씌울 수 있는 상황이라면 아래와 같이 2 byte만 변조하고 나머지는 남겨두면서 onegadget 실행이 가능하기에 base address를 구할 필요가 없어진다.
$ one_gadget /lib/x86_64-linux-gnu/libc.so.6 --near exit,mkdir
# [OneGadget] Gadgets near exit(0x43120):
# 0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
# constraints:
# rsp & 0xf == 0
# rcx == NULL
#
# 0x4f322 execve("/bin/sh", rsp+0x40, environ)
# constraints:
# [rsp+0x40] == NULL
#
# 0x10a38c execve("/bin/sh", rsp+0x70, environ)
# constraints:
# [rsp+0x70] == NULL
#
# [OneGadget] Gadgets near mkdir(0x10fbb0):
# 0x10a38c execve("/bin/sh", rsp+0x70, environ)
# constraints:
# [rsp+0x70] == NULL
#
# 0x4f322 execve("/bin/sh", rsp+0x40, environ)
# constraints:
# [rsp+0x40] == NULL
#
# 0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
# constraints:
# rsp & 0xf == 0
# rcx == NULL
아래와 같이 함수 명을 전체 다 쓰지 않아도 관련 내역을 전부 찾아준다.
$ one_gadget /lib/x86_64-linux-gnu/libc.so.6 --near 'write.*' --raw
# [OneGadget] Gadgets near writev(0x1166a0):
# 1090444 324386 324293
#
# [OneGadget] Gadgets near write(0x110140):
# 1090444 324386 324293
write*이 아니라 write.*임을 잊지 말자.
추가로 elf 파일과 함께 실행해주면, elf 파일 내 존재하는 got 주변의 값을 모두 찾아준다.
$ one_gadget /lib/x86_64-linux-gnu/libc.so.6 --near spec/data/test_near_file.elf --raw
# [OneGadget] Gadgets near exit(0x43120):
# 324293 324386 1090444
#
# [OneGadget] Gadgets near puts(0x809c0):
# 324386 324293 1090444
#
# [OneGadget] Gadgets near printf(0x64e80):
# 324386 324293 1090444
#
# [OneGadget] Gadgets near strlen(0x9dc70):
# 324386 324293 1090444
#
# [OneGadget] Gadgets near __cxa_finalize(0x43520):
# 324293 324386 1090444
#
# [OneGadget] Gadgets near __libc_start_main(0x21ab0):
# 324293 324386 1090444
3.2.2. -r, --raw
출력된 값을 모두 정리해서 offset 값만 출력해준다.
$ one_gadget /lib/x86_64-linux-gnu/libc.so.6 --near 'write.*' --raw
# [OneGadget] Gadgets near writev(0x1166a0):
# 1090444 324386 324293
#
# [OneGadget] Gadgets near write(0x110140):
# 1090444 324386 324293
다만 10진수로 출력해준다.
3.2.3. -l, --level
기본적으로 onegadget은 constraint가 쉬운 몇가지 gadget을 출력해준다.
이를 level로 정의하고 있으며, default 값은 0이고, 1로 변경하면 모든 gadget을 출력해준다.
(level을 0, 1이 끝인듯 변경하는 것은 의미 없다)
로컬에서 테스트 해봤는데 onegadget이 이렇게 많은 줄 몰랐다;;;
┌──(kali㉿kali)-[~/Downloads]
└─$ one_gadget /lib/x86_64-linux-gnu/libc.so.6 -l 0
0xf3f9a posix_spawn(rsp+0x64, "/bin/sh", [rsp+0x38], 0, rsp+0x70, [rsp+0xf0])
constraints:
[rsp+0x70] == NULL
[[rsp+0xf0]] == NULL || [rsp+0xf0] == NULL
[rsp+0x38] == NULL || (s32)[[rsp+0x38]+0x4] <= 0
0xf3fa2 posix_spawn(rsp+0x64, "/bin/sh", [rsp+0x38], 0, rsp+0x70, r9)
constraints:
[rsp+0x70] == NULL
[r9] == NULL || r9 == NULL
[rsp+0x38] == NULL || (s32)[[rsp+0x38]+0x4] <= 0
0xf3fa7 posix_spawn(rsp+0x64, "/bin/sh", rdx, 0, rsp+0x70, r9)
constraints:
[rsp+0x70] == NULL
[r9] == NULL || r9 == NULL
rdx == NULL || (s32)[rdx+0x4] <= 0
┌──(kali㉿kali)-[~/Downloads]
└─$ one_gadget /lib/x86_64-linux-gnu/libc.so.6 -l 1
0x4a1a3 posix_spawn(rdi, "/bin/sh", rdx, rcx, rsp+0x50, environ)
constraints:
rax == NULL
rdi == NULL || writable: rdi
rdx == NULL || (s32)[rdx+0x4] <= 0
rcx == NULL || (u16)[rcx] == NULL
0x4a1b9 posix_spawn(rdi, "/bin/sh", rdx, rcx, r8, environ)
constraints:
[r8] == NULL
rdi == NULL || writable: rdi
rdx == NULL || (s32)[rdx+0x4] <= 0
rcx == NULL || (u16)[rcx] == NULL
0x75eac posix_spawn(rbx+0xe0, "/bin/sh", r12, rcx, r8, environ)
constraints:
[r8] == NULL
rbx+0xe0 == NULL || writable: rbx+0xe0
r12 == NULL || (s32)[r12+0x4] <= 0
rcx == NULL || (u16)[rcx] == NULL
0x75ebb posix_spawn(rbx+0xe0, "/bin/sh", rdx, rcx, r8, environ)
constraints:
[r8] == NULL
rbx+0xe0 == NULL || writable: rbx+0xe0
rdx == NULL || (s32)[rdx+0x4] <= 0
rcx == NULL || (u16)[rcx] == NULL
0xf3f9a posix_spawn(rsp+0x64, "/bin/sh", [rsp+0x38], 0, rsp+0x70, [rsp+0xf0])
constraints:
[rsp+0x70] == NULL
[[rsp+0xf0]] == NULL || [rsp+0xf0] == NULL
[rsp+0x38] == NULL || (s32)[[rsp+0x38]+0x4] <= 0
0xf3fa2 posix_spawn(rsp+0x64, "/bin/sh", [rsp+0x38], 0, rsp+0x70, r9)
constraints:
[rsp+0x70] == NULL
[r9] == NULL || r9 == NULL
[rsp+0x38] == NULL || (s32)[[rsp+0x38]+0x4] <= 0
0xf3fa7 posix_spawn(rsp+0x64, "/bin/sh", rdx, 0, rsp+0x70, r9)
constraints:
[rsp+0x70] == NULL
[r9] == NULL || r9 == NULL
rdx == NULL || (s32)[rdx+0x4] <= 0
0xf3fb1 posix_spawn(rdi, "/bin/sh", rdx, 0, r8, r9)
constraints:
[r8] == NULL
[r9] == NULL || r9 == NULL
rdi == NULL || writable: rdi
rdx == NULL || (s32)[rdx+0x4] <= 0
3.2.4 -f, -s
옛날엔 썼던 메뉴인 것 같은데, 지금은 update 되면서 사용되지 않는 메뉴인 것 같다.
대략 -f는 build id를 무시하고 onegadget 찾기
-s는 찾아낸 onegadget의 주소를 반복해서 삽입하며 스크립트 실행
하는 메뉴이다.
아무래도 pwntools가 나오고나서 사라진 듯.
4. with python
파이썬과 함께 onegadget 사용이 가능하다.
예제는 아래와 같다.
from pwn import *
import subprocess
def one_gadget(filename):
return [int(i) for i in subprocess.check_output(['one_gadget', '--raw', filename]).decode().split(' ')]
file = '/lib/x86_64-linux-gnu/libc.so.6'
ogt_offset = one_gadget(file)
print(ogt_offset)
┌──(kali㉿kali)-[~/Downloads]
└─$ python a.py
[999322, 999330, 999335]
'Tips & theory' 카테고리의 다른 글
system hacking을 위한 docker 설치 및 사용법 (0) | 2022.09.05 |
---|---|
Return to csu (0) | 2022.09.05 |
함수 호출 규약. (0) | 2022.09.05 |
system hacking을 register 기초 (0) | 2022.09.05 |
_IO_FILE_plus의 모든 것. (0) | 2022.08.21 |