Pwntools Tutorial - ELFs

모씨김(mossikim)·2025년 8월 13일

Pwntools

목록 보기
4/8
post-thumbnail

ELFs


  • Loading ELF Files
  • Using Symbols
  • Changing the Base Address
  • Reading ELF Files
  • Patching ELF Files
  • Searching ELF Files
  • Building ELF Files
  • Running and Debugging ELF Files


Loading Elf Files

ELF 파일들은 경로에 의해 로드된다. 로드되면, 파일의 몇몇 보안 속성이 출력된다.

ELF(’file’) 로 파일 오브젝트를 얻을 수 있다.

from pwn import *

e = ELF('/bin/bash')
# [*] '/bin/bash'
#     Arch:     amd64-64-little
#     RELRO:    Partial RELRO
#     Stack:    Canary found
#     NX:       NX enabled
#     PIE:      No PIE
#     FORTIFY:  Enabled

Using Symbols

ELF 파일에는 몇가지 사용 가능한 심볼 세트가 있으며, 각각은 딕셔너리 형태로 포함된다.

  • ELF.symbols 는 알려진 모든 심볼들을 나열한다. GOT, PLT entry가 포함된다.
  • ELF.got 는 GOT entry만 포함한다.
  • ELF.plt 는 PLT entry만 포함한다.
  • ELF.functions 는 오직 함수들만 포함한다. (DWARF 심볼을 요구함)
from pwn import *

e = ELF('/bin/bash')

print "%#x -> license" % e.symbols['bash_license']
print "%#x -> execve" % e.symbols['execve']
print "%#x -> got.execve" % e.got['execve']
print "%#x -> plt.execve" % e.plt['execve']
print "%#x -> list_all_jobs" % e.functions['list_all_jobs'].address

위 예제의 결과는 다음과 같다 :

0x4ba738 -> license
0x41db60 -> execve
0x6f0318 -> got.execve
0x41db60 -> plt.execve
0x446420 -> list_all_jobs

Changing the Base Address

ELF의 Base Address를 변경할 수 있다. ( ASLR 조정 등 )

from pwn import *

e = ELF('/bin/bash')

print "%#x -> base address" % e.address
print "%#x -> entry point" % e.entry
print "%#x -> execve" % e.symbols['execve']

print "---"
e.address = 0x12340000

print "%#x -> base address" % e.address
print "%#x -> entry point" % e.entry
print "%#x -> execve" % e.symbols['execve']

위 예제의 결과는 다음과 같다 :

0x400000 -> base address
0x42020b -> entry point
0x41db60 -> execve
---
0x12340000 -> base address
0x1236020b -> entry point
0x1235db60 -> execve

Reading ELF Files

우리는 read, write, 그리고 packing 모듈 등을 활용해 ELF 파일에 직접적으로 작용을 할 수 있다. 추가적으로, disasm method 를 활용해 디스어셈블리 또한 볼 수 있다.

from pwn import *

e = ELF('/bin/bash')

print repr(e.read(e.address, 4))

p_license = e.symbols['bash_license']
license   = e.unpack(p_license)
print "%#x -> %#x" % (p_license, license)

print e.read(license, 14)
print e.disasm(e.symbols['main'], 12)

위 예제의 결과는 다음과 같다 :

'\x7fELF'
0x4ba738 -> 0x4ba640
License GPLv3+
  41eab0:       41 57                   push   r15
  41eab2:       41 56                   push   r14
  41eab4:       41 55                   push   r13

Patching ELF Files

ELF 파일들을 패치하는 것은 단순하다.

from pwn import *

e = ELF('/bin/bash')

# Cause a debug break on the 'exit' command
e.asm(e.symbols['exit_builtin'], 'int3')

# Disable chdir and just print it out instead
e.pack(e.got['chdir'], e.plt['puts'])

# Change the license
p_license = e.symbols['bash_license']
license = e.unpack(p_license)
e.write(license, 'Hello, world!\n\x00')

e.save('./bash-modified')

우리는 이제 우리의 수정된 버전의 배시를 실행할 수 있다.

$ chmod +x ./bash-modified
$ ./bash-modified -c 'exit'
Trace/breakpoint trap (core dumped)
$ ./bash-modified --version | grep "Hello"
Hello, world!
$ ./bash-modified -c 'cd "No chdir for you!"'
/home/user/No chdir for you!
No chdir for you!
./bash-modified: line 0: cd: No chdir for you!: No such file or directory

Searching ELF Files

search를 활용해 바이트 시퀀스를 검색하고, 바이트 시퀀스의 주소를 얻을 수 있다.

from pwn import *

e = ELF('/bin/bash')

for address in e.search('/bin/sh\x00'):
    print hex(address)

위 예제의 결과는 다음과 같다 :

0x420b82
0x420c5e

Building ELF Files

ELF 파일들은 쉽게(?) 생성될 수 있다. 모든 함수들은 context를 참조한다. 관련 함수는 from_bytes 와 from_assembly 가 있다. 각각은 ELF 오브젝트를 반환하며, 이것은 쉽게 파일로 저장될 수 있다.

이 기능은 잘 모르겠어서 추가적인 서술은 안함. 밑은 공식 예제

from pwn import *

ELF.from_bytes('\xcc').save('int3-1')
ELF.from_assembly('int3').save('int3-2')
ELF.from_assembly('nop', arch='powerpc').save('powerpc-nop')

Running and Debugging ELF Files

만약 ELF object를 보유하고 있다면, 실행이나 디버깅을 직접 할 수 있다.

# process 실행
>>> io = elf.process()
# vs
>>> io = process(elf.path)
# process 디버깅
>>> io = elf.debug()
# vs
>>> io = gdb.debug(elf.path)

Pwntools - official tutorial
Full Reference

0개의 댓글