Latest Tweets

shc: the expiration date restriction is easily bypassed

The issue

It’s been a long time since someone has written something about the shc utility, a linux utility that transforms a shell script in any language into a stripped binary executable. Apart from creating a stripped binary you can distribute and execute on any GNU/Linux computer that has the same utilities and shells the original script relies upon, you can set an execution deadline as well.

So, let’s imagine we have this simple and trivial bash shell-script and we compile it so that no-one will be executing it after October, the 20th:

#!/bin/bash
echo “SHC is a good tool….”
exit 0

# shc -f test.sh -e 20/10/2015

# ./test.sh.x
./test.sh.x: has expired!
Please contact your provider

Well, so far so good. If we try to execute the stripped binary, we get an expiration date error message.

Analysing the binary

The shc utility stores the expiration date inside the stripped binary, then somewhere in the code compares it with the current date to determine whether the execution can take place or not. We are going to disassemble the stripped binary executable using objdump in search of calls to time, localtime, and the sort:

objdump -d test.sh.x|grep time
00000000004009c0 <time@plt>:
401017: e8 a4 f9 ff ff callq 4009c0 <time@plt>

Got it! We can disassemble the whole code, save it in a file and go directly to its offset 0x401017:

Testing if the current date is greater than the expiration one.

Testing if the current date is greater than the expiration one.

If we run the code inside a gdb debugging session, we can get the values for RAX and RBX. These registers hold UNIX timestamps for the current date (RAX), and the expiration date (RBX), as shown next:

(gdb) b *0x401017
Breakpoint 3 at 0x401017
(gdb) run
Breakpoint 3, 0x0000000000401017 in xsh ()
(gdb) x/i $rip
=> 0x401017 <xsh+95>: call 0x4009c0 <time@plt>
(gdb) ni
0x000000000040101c in xsh ()
(gdb) x/i $rip
=> 0x40101c <xsh+100>: cmp rbx,rax
(gdb) p $rax
$5 = 1445444418
(gdb) p $rbx
$6 = 1445295600

If we convert these two values, we’ve got:

(gdb) shell date -d @1445444418
Wed 21 Oct 18:20:18 CEST 2015
(gdb) shell date -d @1445295600
Tue 20 Oct 01:00:00 CEST 2015

As clearly seen, the value for the RBX register is the expiration date of October, the 20th. Therefore, whenever RAX >= RBX, that is, whenever the current date is greater than or equal the expiration date, the JGE (Jump If Greater Or Equal) instruction will be executed and our expiration error message shown.

Patching the opcode to avoid jumping

What we need to do in order to avoid jumping is to replace the JGE instruction with JL (Jump If Less). The opcode for JL is 0x7C. Using hexeditor, we open the test.sh.x binary and look for these bytes:

48 39 c3 7d 0a

These bytes represents the cmp %rax,%rbx and jge instructions, as clearly seen on the previous disassembled code. Once we have located these bytes, we need to replace 0x7d (JGE instruction) with 0x7c (JL instruction):

Replacing JGE (0x7D) with JL (0x7C)

Replacing JGE (0x7D) with JL (0x7C)

Now, if we save the new binary and run it we’ve got it executed:

./test.sh.x
SHC is a good tool….

So far so good. It turns out that this restriction is easy as pie to bypass.

How to patch any shc stripped binary executable with an expiration date restriction

If we are talking 64 bit architecture, just open the binary with hexeditor and look for the bytes already mentioned (you can add more bytes to ensure you are on the right spot, if you want) and replace byte 0x7D with byte 0x7C. Save the file and that’s it! Let’s imagine we have another script, called mailsize.sh. Next screenshot illustrates this:

The opcodes are always the same, no matter what the original script looks like. These bytes are for X86_64 (RAX and RBX).

The opcodes are always the same, no matter what the original script looks like. These bytes are for X86_64 (RAX and RBX).