sashs / Ropper

Display information about files in different file formats and find gadgets to build rop chains for different architectures (x86/x86_64, ARM/ARM64, MIPS, PowerPC, SPARC64). For disassembly ropper uses the awesome Capstone Framework.
https://scoding.de/ropper
BSD 3-Clause "New" or "Revised" License
1.87k stars 206 forks source link

Gadget address for vmlinux ELF x86_64 off by 0x200000 #125

Closed bcoles closed 4 years ago

bcoles commented 4 years ago

I'm not convinced #107 was entirely resolved by e0f704faaa05b537fbb678df7c4e3a172918f63d. The same section.offset code pattern is in use in a few places, and I recently ran into this issue using RopperService.searchInstructions on Ropper 1.13.5.

Note discrepancy below of 0x200000L between the gadgets returned by RopperService.searchInstructions versus the gadgets returned by CLI with the --instructions flag.

It's possible that the discrepancy is due to Python environment, but the sanity check implies otherwise.

test@ubuntu-16-04-x64:~/Desktop/Ropper$ git diff ropper/rop.py
diff --git a/ropper/rop.py b/ropper/rop.py
index e176009..fa2fda9 100644
--- a/ropper/rop.py
+++ b/ropper/rop.py
@@ -216,6 +216,7 @@ class Ropper(object):
         toReturn = []
         code = bytearray(section.bytes)
         offset = section.offset
+        print("sanity check - we are using this lib file")
         for match in re.finditer(opcode, code):
             opcodeGadget = Gadget(binary.checksum, section.name, binary.arch)

test@ubuntu-16-04-x64:~/Desktop/Ropper$ python3 -m ropper --file /home/test/Desktop/vmlinux --instructions 'mov rdi, rax ; pop rbx ; mov rax, rdi ; pop r12 ; pop rbp ; ret'
sanity check - we are using this lib file
sanity check - we are using this lib file

Instructions
============

0xffffffff8117d5d7: mov rdi, rax; pop rbx; mov rax, rdi; pop r12; pop rbp; ret; 

1 gadgets found
test@ubuntu-16-04-x64:~/Desktop/Ropper$ python3 ./sample.py 
sanity check - we are using this lib file
0xffffffff8137d5d7: mov rdi, rax; pop rbx; mov rax, rdi; pop r12; pop rbp; ret; 
test@ubuntu-16-04-x64:~/Desktop/Ropper$ python -c "print(hex(0xffffffff8137d5d7 - 0xffffffff8117d5d7))"
0x200000L
test@ubuntu-16-04-x64:~/Desktop/Ropper$ cat sample.py 
#!/usr/bin/env python3
import ropper
from ropper import RopperService

# not all options need to be given
options = {
  'color' : True,
  'badbytes': '00',
  'all' : True,
  'type' : 'all',
  'detailed' : False
}

rs = RopperService(options)

f = '/home/test/Desktop/vmlinux'
rs.addFile('vmlinux', bytes=open(f, 'rb').read(), raw=True, arch='x86_64')

code = 'mov rdi, rax ; pop rbx ; mov rax, rdi ; pop r12 ; pop rbp ; ret'

rs.setImageBaseFor(name='vmlinux', imagebase=0xffffffff81000000)

gadgets_dict = rs.searchInstructions(code=code, name='vmlinux')

for file, gadgets in gadgets_dict.items():
    for g in gadgets:
        print(g)

test@ubuntu-16-04-x64:~/Desktop/Ropper$ python3 -m ropper -v
Version: Ropper 1.13.5
Author: Sascha Schirra
Website: http://scoding.de/ropper

test@ubuntu-16-04-x64:~/Desktop/Ropper$ 
sashs commented 4 years ago

Hey,

you are right. I will check that again

sashs commented 4 years ago

Hey, Sorry for the very long delay. I pushed a "fix" which should fix this issue.

bcoles commented 4 years ago

Thanks @sashs

I tried out the patch but ran into the same issue. Quick tests - willing to admit user error. I'll review.

test@ubuntu-16-04-x64:~/Desktop/Ropper$ git pull
Already up-to-date.

test@ubuntu-16-04-x64:~/Desktop/Ropper$ git diff ropper/rop.py
diff --git a/ropper/rop.py b/ropper/rop.py
index 6c5722d..ad64c33 100644
--- a/ropper/rop.py
+++ b/ropper/rop.py
@@ -217,6 +217,7 @@ class Ropper(object):
         code = bytearray(section.bytes)
         # TODO: Another solution should be used here. This is a hack for compatibility reasons. to resolve the gadget address calculation of segments of elf files have a different base address if calculated segment.virtualAddress - segment.offset 
         offset = section.offset - (binary.originalImageBase - (section.virtualAddress - section.offset))
+        print("sanity check - we are using this lib file (again)")
         for match in re.finditer(opcode, code):
             opcodeGadget = Gadget(binary.checksum, section.name, binary.arch)

test@ubuntu-16-04-x64:~/Desktop/Ropper$ cd ../
test@ubuntu-16-04-x64:~/Desktop$ python3 ./Ropper/sample.py 
sanity check - we are using this lib file (again)
0xffffffff81378157: mov rdi, rax; pop rbx; mov rax, rdi; pop r12; pop rbp; ret; 

test@ubuntu-16-04-x64:~/Desktop$ python3 -m ropper --file /home/test/Desktop/vmlinux --instructions 'mov rdi, rax ; pop rbx ; mov rax, rdi ; pop r12 ; pop rbp ; ret'
sanity check - we are using this lib file (again)
sanity check - we are using this lib file (again)

Instructions
============

0xffffffff81178157: mov rdi, rax; pop rbx; mov rax, rdi; pop r12; pop rbp; ret; 

1 gadgets found
test@ubuntu-16-04-x64:~/Desktop$ 
bcoles commented 4 years ago

It looks like your patch is the same hack copied from the previous patch. #107

I vaguely recall trying the same previously and running into issues, else I would have created a PR.

The offset appears to be calculated differently when invoked by RopperService.

test@ubuntu-16-04-x64:~/Desktop/Ropper$ git diff ropper/rop.py
diff --git a/ropper/rop.py b/ropper/rop.py
index 6c5722d..293f8e0 100644
--- a/ropper/rop.py
+++ b/ropper/rop.py
@@ -217,6 +217,8 @@ class Ropper(object):
         code = bytearray(section.bytes)
         # TODO: Another solution should be used here. This is a hack for compatibility reasons. to resolve the gadget address calculation of segments of elf files have a different base address if calculated segment.virtualAddress - segment.offset 
         offset = section.offset - (binary.originalImageBase - (section.virtualAddress - section.offset))
+        print("offset: %s" % offset)
+        print("sanity check - we are using this lib file (again)")
         for match in re.finditer(opcode, code):
             opcodeGadget = Gadget(binary.checksum, section.name, binary.arch)

test@ubuntu-16-04-x64:~/Desktop/Ropper$ python3 -m ropper --file /home/test/Desktop/vmlinux --instructions 'mov rdi, rax ; pop rbx ; mov rax, rdi ; pop r12 ; pop rbp ; ret'
offset: 2097152
sanity check - we are using this lib file (again)
offset: 18190336
sanity check - we are using this lib file (again)

Instructions
============

0xffffffff81178157: mov rdi, rax; pop rbx; mov rax, rdi; pop r12; pop rbp; ret; 

1 gadgets found
test@ubuntu-16-04-x64:~/Desktop/Ropper$ python3 ./sample.py 
offset: 0
sanity check - we are using this lib file (again)
0xffffffff81378157: mov rdi, rax; pop rbx; mov rax, rdi; pop r12; pop rbp; ret; 
test@ubuntu-16-04-x64:~/Desktop/Ropper$ 

It is also kind of confusing that the Ropper can be invoked via command line without the need to specify the image base address; where as RopperService refuses to identify instructions unless the imagebase is defined with setImageBaseFor.

rs.setImageBaseFor(name='vmlinux', imagebase=0xffffffff81000000)
bcoles commented 4 years ago

It appears as though binary and section are never populated when searchOpcode is called via RopperService (via searchInstructions).

test@ubuntu-16-04-x64:~/Desktop/Ropper$ git diff ropper/rop.py
diff --git a/ropper/rop.py b/ropper/rop.py
index 6c5722d..f813520 100644
--- a/ropper/rop.py
+++ b/ropper/rop.py
@@ -217,6 +217,10 @@ class Ropper(object):
         code = bytearray(section.bytes)
         # TODO: Another solution should be used here. This is a hack for compatibility reasons. to resolve the gadget address calculation of segments of elf files have a different base address if calculated segment.virtualAddress - segment.offset 
         offset = section.offset - (binary.originalImageBase - (section.virtualAddress - section.offset))
+        print("binary.originalImageBase: %s" % binary.originalImageBase)
+        print("section.offset: %s" % section.offset)
+        print("section.virtualAddress: %s" % section.virtualAddress)
+        print("sanity check - we are using this lib file (again)")
         for match in re.finditer(opcode, code):
             opcodeGadget = Gadget(binary.checksum, section.name, binary.arch)

test@ubuntu-16-04-x64:~/Desktop/Ropper$ python3 -m ropper --file /home/test/Desktop/vmlinux --instructions 'mov rdi, rax ; pop rbx ; mov rax, rdi ; pop r12 ; pop rbp ; ret'
binary.originalImageBase: 18446744071576748032
section.offset: 2097152
section.virtualAddress: 18446744071578845184
sanity check - we are using this lib file (again)
binary.originalImageBase: 18446744071576748032
section.offset: 20287488
section.virtualAddress: 18446744071594938368
sanity check - we are using this lib file (again)

Instructions
============

0xffffffff81178157: mov rdi, rax; pop rbx; mov rax, rdi; pop r12; pop rbp; ret; 

1 gadgets found
test@ubuntu-16-04-x64:~/Desktop/Ropper$ python3 ./sample.py 
binary.originalImageBase: 0
section.offset: 0
section.virtualAddress: 0
sanity check - we are using this lib file (again)
0xffffffff81378157: mov rdi, rax; pop rbx; mov rax, rdi; pop r12; pop rbp; ret; 
test@ubuntu-16-04-x64:~/Desktop/Ropper$ 

Am I invoking the RopperService incorrectly ? (sample.py source in original post)

sashs commented 4 years ago

Yes, you do. I am preparing a right example.

sashs commented 4 years ago

This is a right example.

#!/usr/bin/env python3
import ropper
from ropper import RopperService

rs = RopperService()

f = "./vmlinux"
rs.addFile(f)

code = 'mov rdi, r12; call rax'

gadgets_dict = rs.searchInstructions(code=code, name=f)

for file, gadgets in gadgets_dict.items():
    for g in gadgets:
        print(g)

However, I see there is bug if you try to load the file with the "bytes"

Yes, at the moment I used the same hack. It is not nice, but should work for the moment.

EDIT: I used another instruction

bcoles commented 4 years ago

Thanks. That works.

$ python3 sample2.py 
binary.originalImageBase: 18446744071576748032
section.offset: 2097152
section.virtualAddress: 18446744071578845184
sanity check - we are using this lib file (again)
binary.originalImageBase: 18446744071576748032
section.offset: 20287488
section.virtualAddress: 18446744071594938368
sanity check - we are using this lib file (again)
0xffffffff81178157: mov rdi, rax; pop rbx; mov rax, rdi; pop r12; pop rbp; ret; 

I don't need to read files with bytes.

If you want to patch the bytes issue, then I'll leave this issue open, and test your patch.

sashs commented 4 years ago

No, this issue can be closed, since this is an issue of the filebytes lib and not of ropper.