Puzzle 2 - Making Of
I'm not going to give a tutorial on dynamic linking in Linux here (largely because I don't understand most of it), but for our purposes there are only a few bits of information to know.
- Library functions are called via indirection.
- The main code calles a linkage stub.
- Library function addresses are stored in a table.
- This table is lazily filled in at run-time
When looking at a program using a disassembler, all you see is that it calls the linkage stub. This linkage stub jumps directly to the position stored in the function table. If this entry of the function table hasn't been set up yet, the stub calls the dynamic loader asking it to fill in the correct address and call the function while it's at it. From then on the linkage stub will be able to go straight to the library function.
Checking whether the function table has been set up yet, and defaulting to jumping to the function if it has is done in a rather clever way. For example let's look at the linkage stub table in our puzzle binary.
vshcmd: > objdump -j .plt -d test_password
test_password: file format elf64-x86-64
Disassembly of section .plt:
0000000000400420 <.plt>:
400420: ff 35 e2 0b 20 00 pushq 0x200be2(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
400426: ff 25 e4 0b 20 00 jmpq *0x200be4(%rip) # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
40042c: 0f 1f 40 00 nopl 0x0(%rax)
0000000000400430 <puts@plt>:
400430: ff 25 e2 0b 20 00 jmpq *0x200be2(%rip) # 601018 <puts@GLIBC_2.2.5>
400436: 68 00 00 00 00 pushq $0x0
40043b: e9 e0 ff ff ff jmpq 400420 <.plt>
0000000000400440 <strcmp@plt>:
400440: ff 25 da 0b 20 00 jmpq *0x200bda(%rip) # 601020 <strcmp@GLIBC_2.2.5>
400446: 68 01 00 00 00 pushq $0x1
40044b: e9 d0 ff ff ff jmpq 400420 <.plt>
fake_plt [17:38:30] $
Looking at the strcmp@plt
entry, we see that it
unconditionally jumps to the address stored in 0x601020
.
Calling into the dynamic loader in order to handle setting up the offset
table is handled by the three instructions at the .plt
, and
that is gotten to by the last instruction in each @plt
stub.
That last instruction is reached because the function table in the binary
itself starts with the location in memory of the pushq
instruction in each corresponding stub.
When the stub is first called, it jumps straight to the
pushq
instruction that follows it, so that the
initialisation is performed.
From then on the offset stored in the function table is the correct
location of the library function, so the jmpq
instruction
goes there, and the initialisation code isn't ran.
Playing Tricks
Following the discussion above, we can see that if we were to change addresses in the function table as it is stored in the file, then instead of falling through to the initialisation code, the linkage stub will correctly jump to the position we define. There is no change in the calling code, so as far as it knows it's calling the library function, and the tricks we're playing are hidden behind the linkage indirection.
This means that from plain disassembly of instructions no-one can tell the difference between our trick binary and a normal one. Moreover many people used to stepping through instructions in an executable will not notice anything strange (apart from the outcome) the first time they debug it, as people get accustomed to skipping over known library functions.
The second or third time through a debugger someone might notice that
strcmp()
isn't doing what it should, and stepi into the
other function.
I used this by overwriting the strcmp@plt
entry in the
function table with the position of a function in the main binary that has
the same calling signature but different functionality.
I gave this function a name that sounds internal __exit_elf64
and made it check that string 1 was what you get from decrementing every
character in string 2.
Hence when check_password()
called
strcmp("etmrhdr ", password)
it was actually checking that
password
was equal to "funsies!"
.
The specifics of how to do that can be seen in these vsh commands that use this C program
For much more along these lines see my post on overriding dynamically linked functions.