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.

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.