• medium

(╯°□°)╯︵ ┻━┻

author: tk

Attached file:

Writeup

smiley is a stripped ELF64 binary, so I’ve just dropped it into IDA

There was an easy-to-patch check of the current date, after that, it asks for a password and checks it.

A few checks were simple, so I knew that key[3]=key[21]=key[35]=" ", key[0]="o", key[1]="_" and key[2]="O".

After that, I’ve found a loop that generated indexes with a crazy function with lots of bit math (but actually just generated 5,6,7,…20), and XORed the 4,5,…19th bytes of the key with those and compared the result with a stored one.

I’ve put this one aside when I’ve seen that one of the stored values was bigger than 128, thus one of the bytes was not in ascii range…

I’ve come back later, and found another check I’ve missed the first time: key[20]=")" - and from that and the XORs we can just calculate most of the characters! I’ve realized when I saw it that it’s not ASCII, but UTF-8…

After that, it takes the next unchecked byte, uses it as a single-byte XOR decrypt key for some data, and executes it as code, passing it the remaining key as a parameter. Then it does it again with another piece of data and another byte of the key.

I’ve written a python script that tries every byte and checks if the resulting code causes a segfault to narrow down the possibilities:

# patched XOR checks bc I did not know that it's UTF-8

key_pat = list("o_O Apxxxxxxxxxxxxxxx xxxxxxxxxxxxx xxxxxxxx".encode())

from pwn import *

def test_key(k):
    p = process("smiley")
    p.clean()
    p.sendline(k)
    x = p.clean()
    if x:
        return True
    return False
    #print(p.clean())


for i in range(256):
    k = key_pat[:]
    k[0x16] = i
    k[0x14] = ord(")")
    key = bytes(k)
    if test_key(key):
        print(f"Possible: {i}")
        print(key.decode())

(and also for the other byte)

After that, I’ve tried all of them and used GDB to disassemble the decoded instructions, and this way I’ve found the only real bytes.

The decrypted programs just perform some checks on the key:

   0x5555555580a0:      push   rbp
   0x5555555580a1:      mov    rbp,rsp
   0x5555555580a4:      mov    QWORD PTR [rbp-key_part],rdi ; -0x18
   0x5555555580a8:      movabs rax,0x298483e3285f5caf
   0x5555555580b2:      mov    QWORD PTR [rbp-DATA],rax ; -0x10
   0x5555555580b6:      mov    DWORD PTR [rbp-0x8],0xafc22f5f ; -0x8
   0x5555555580bd:      mov    DWORD PTR [rbp-COUNTER],0x0
   0x5555555580c4:      jmp    0x5555555580ef ; FOR_CHECK
   FOR_BODY
   0x5555555580c6:      mov    eax,DWORD PTR [rbp-COUNTER]
   0x5555555580c9:      movsxd rdx,eax
   0x5555555580cc:      mov    rax,QWORD PTR [rbp-key_part]
   0x5555555580d0:      add    rax,rdx 
   0x5555555580d3:      movzx  edx,BYTE PTR [rax] ; KEY_PART[COUNTER]
   0x5555555580d6:      mov    eax,DWORD PTR [rbp-COUNTER]
   0x5555555580d9:      cdqe
   0x5555555580db:      movzx  eax,BYTE PTR [rbp+rax*1-DATA] ; DATA[COUNTER]
   0x5555555580e0:      cmp    dl,al
   0x5555555580e2:      je     0x5555555580eb $ FOR_CHECK
   NOT_EQ
   0x5555555580e4:      mov    eax,0x0
   0x5555555580e9:      jmp    0x5555555580fa ; RET
   FOR_INCREMENT
   0x5555555580eb:      add    DWORD PTR [rbp-COUNTER],0x1
   FOR_CHECK
   0x5555555580ef:      cmp    DWORD PTR [rbp-COUNTER],0xb
   0x5555555580f3:      jle    0x5555555580c6 ; FOR_BODY
   FOR_END
   0x5555555580f5:      mov    eax,0x1
   RET
   0x5555555580fa:      pop    rbp
   0x5555555580fb:      ret
0x555555558100:      push   rbp
0x555555558101:      mov    rbp,rsp
0x555555558104:      mov    QWORD PTR [rbp-KEY_PART],rdi ; -0x18
0x555555558108:      mov    DWORD PTR [rbp-0x7],0xb0835528
0x55555555810f:      mov    WORD PTR [rbp-0x3],0x4bd7
0x555555558115:      mov    BYTE PTR [rbp-0x1],0x74
0x555555558119:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x55555555811d:      movzx  edx,BYTE PTR [rax]
0x555555558120:      movzx  eax,BYTE PTR [rbp-0x7]
0x555555558124:      cmp    dl,al ; KEY_PART [0] == DATA[0] = 0x28
0x555555558126:      je     0x555555558132

0x555555558128:      mov    eax,0x0
0x55555555812d:      jmp    0x5555555582eb ; RET 0

0x555555558132:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x555555558136:      movzx  eax,BYTE PTR [rax]
0x555555558139:      movsx  edx,al ; KEY_PART[0]
0x55555555813c:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x555555558140:      add    rax,0x1
0x555555558144:      movzx  eax,BYTE PTR [rax]
0x555555558147:      movsx  eax,al
0x55555555814a:      add    edx,eax ; KEY_PART[0] + KEY_PART[1]
0x55555555814c:      movzx  eax,BYTE PTR [rbp-0x6] ; DATA[1] = 0x55
0x555555558150:      movsx  eax,al
0x555555558153:      cmp    edx,eax ; 0x55-0x28 -> 0x2d
0x555555558155:      je     0x555555558161

0x555555558157:      mov    eax,0x0
0x55555555815c:      jmp    0x5555555582eb ; RET 0

0x555555558161:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x555555558165:      movzx  eax,BYTE PTR [rax]
0x555555558168:      mov    edx,eax ; KEY_PART[0]
0x55555555816a:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x55555555816e:      add    rax,0x1
0x555555558172:      movzx  eax,BYTE PTR [rax]
0x555555558175:      add    edx,eax ; KEY_PART[0] + KEY_PART[1]
0x555555558177:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x55555555817b:      add    rax,0x2
0x55555555817f:      movzx  eax,BYTE PTR [rax]
0x555555558182:      add    eax,edx ; KEY_PART[0] + KEY_PART[1] + KEY_PART[2]
0x555555558184:      movzx  edx,BYTE PTR [rbp-0x5] ; DATA[3] = 0x83
0x555555558188:      cmp    al,dl ; 0x83 - 0x55 = 0x2e
0x55555555818a:      je     0x555555558196

0x55555555818c:      mov    eax,0x0
0x555555558191:      jmp    0x5555555582eb ; RET 0

0x555555558196:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x55555555819a:      movzx  eax,BYTE PTR [rax]
0x55555555819d:      mov    edx,eax
0x55555555819f:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x5555555581a3:      add    rax,0x1
0x5555555581a7:      movzx  eax,BYTE PTR [rax]
0x5555555581aa:      add    edx,eax
0x5555555581ac:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x5555555581b0:      add    rax,0x2
0x5555555581b4:      movzx  eax,BYTE PTR [rax]
0x5555555581b7:      add    edx,eax
0x5555555581b9:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x5555555581bd:      add    rax,0x3
0x5555555581c1:      movzx  eax,BYTE PTR [rax]
0x5555555581c4:      add    eax,edx
0x5555555581c6:      movzx  edx,BYTE PTR [rbp-0x4]
0x5555555581ca:      cmp    al,dl
0x5555555581cc:      je     0x5555555581d8

0x5555555581ce:      mov    eax,0x0
0x5555555581d3:      jmp    0x5555555582eb ; RET 0

0x5555555581d8:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x5555555581dc:      movzx  eax,BYTE PTR [rax]
0x5555555581df:      mov    edx,eax
0x5555555581e1:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x5555555581e5:      add    rax,0x1
0x5555555581e9:      movzx  eax,BYTE PTR [rax]
0x5555555581ec:      add    edx,eax
0x5555555581ee:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x5555555581f2:      add    rax,0x2
0x5555555581f6:      movzx  eax,BYTE PTR [rax]
0x5555555581f9:      add    edx,eax
0x5555555581fb:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x5555555581ff:      add    rax,0x3
0x555555558203:      movzx  eax,BYTE PTR [rax]
0x555555558206:      add    edx,eax
0x555555558208:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x55555555820c:      add    rax,0x4
0x555555558210:      movzx  eax,BYTE PTR [rax]
0x555555558213:      add    eax,edx
0x555555558215:      movzx  edx,BYTE PTR [rbp-0x3]
0x555555558219:      cmp    al,dl
0x55555555821b:      je     0x555555558227

0x55555555821d:      mov    eax,0x0
0x555555558222:      jmp    0x5555555582eb ; RET 0

0x555555558227:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x55555555822b:      movzx  eax,BYTE PTR [rax]
0x55555555822e:      mov    edx,eax
0x555555558230:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x555555558234:      add    rax,0x1
0x555555558238:      movzx  eax,BYTE PTR [rax]
0x55555555823b:      add    edx,eax
0x55555555823d:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x555555558241:      add    rax,0x2
0x555555558245:      movzx  eax,BYTE PTR [rax]
0x555555558248:      add    edx,eax
0x55555555824a:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x55555555824e:      add    rax,0x3
0x555555558252:      movzx  eax,BYTE PTR [rax]
0x555555558255:      add    edx,eax
0x555555558257:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x55555555825b:      add    rax,0x4
0x55555555825f:      movzx  eax,BYTE PTR [rax]
0x555555558262:      add    edx,eax
0x555555558264:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x555555558268:      add    rax,0x5
0x55555555826c:      movzx  eax,BYTE PTR [rax]
0x55555555826f:      add    eax,edx
0x555555558271:      movzx  edx,BYTE PTR [rbp-0x2]
0x555555558275:      cmp    al,dl
0x555555558277:      je     0x555555558280

0x555555558279:      mov    eax,0x0
0x55555555827e:      jmp    0x5555555582eb ; RET 0

0x555555558280:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x555555558284:      movzx  eax,BYTE PTR [rax]
0x555555558287:      mov    edx,eax
0x555555558289:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x55555555828d:      add    rax,0x1
0x555555558291:      movzx  eax,BYTE PTR [rax]
0x555555558294:      add    edx,eax
0x555555558296:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x55555555829a:      add    rax,0x2
0x55555555829e:      movzx  eax,BYTE PTR [rax]
0x5555555582a1:      add    edx,eax
0x5555555582a3:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x5555555582a7:      add    rax,0x3
0x5555555582ab:      movzx  eax,BYTE PTR [rax]
0x5555555582ae:      add    edx,eax
0x5555555582b0:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x5555555582b4:      add    rax,0x4
0x5555555582b8:      movzx  eax,BYTE PTR [rax]
0x5555555582bb:      add    edx,eax
0x5555555582bd:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x5555555582c1:      add    rax,0x5
0x5555555582c5:      movzx  eax,BYTE PTR [rax]
0x5555555582c8:      add    edx,eax
0x5555555582ca:      mov    rax,QWORD PTR [rbp-KEY_PART]
0x5555555582ce:      add    rax,0x6
0x5555555582d2:      movzx  eax,BYTE PTR [rax]
0x5555555582d5:      add    eax,edx
0x5555555582d7:      movzx  edx,BYTE PTR [rbp-0x1]
0x5555555582db:      cmp    al,dl
0x5555555582dd:      je     0x5555555582e6 ; RET_OK

0x5555555582df:      mov    eax,0x0
0x5555555582e4:      jmp    0x5555555582eb ; RET 0
RET_OK
0x5555555582e6:      mov    eax,0x1
RET
0x5555555582eb:      pop    rbp
0x5555555582ec:      ret

(commented and formatted it a bit)

I’ve decided to just use z3 to make the key. Totally unnecessary, but did some thinking for me and I’ve wanted to try it before:

from z3 import *


s = Solver()
key = [BitVec(f"key[{i}]",8) for i in range(44)]

for i in key:
    s.add(i!=0)
    s.add(i!=10)


s.add(key[3]==key[16+5])
s.add(key[16+5]==key[32+3])
s.add(key[32+3]==ord(" "))

s.add(key[0]==ord("o"))
s.add(key[1]==ord("_"))
s.add(key[2]==ord("O"))

XORDATA = (0x51ED9072636CED08).to_bytes(8,"little") + (0x9972636CEDB65C56).to_bytes(8,"little")

for i in range(0x10):
    s.add(key[i+4]^key[i+5]==XORDATA[i])

# checked bytes:
s.add(key[0x14]==ord(")"))
#s.add(key[0x16]==84)
#s.add(key[0x16]==85)
s.add(key[0x16]==194)


SECOND_XOR = [29,82,116,136,226,227]
s.add(key[0x24]==SECOND_XOR[2])

CHECK1 = (0x298483e3285f5caf).to_bytes(8,"little") + (0xafc22f5f).to_bytes(4,"little")

for i, b in enumerate(CHECK1):
    s.add(key[0x17+i]==b)

CHECK2 = (0).to_bytes(1,"little") + (0xb0835528).to_bytes(4,"little") + (0x4bd7).to_bytes(2,"little") + (0x74).to_bytes(1,"little")

for i, b in enumerate(CHECK2[:-1]):
    s.add(key[0x25+i]==CHECK2[i+1]-CHECK2[i])

assert s.check()==sat

d = [0]*44
m = s.model()
for k in m:
    i = int(str(k)[4:-1])
    d[i] = m[k].as_long()
print(bytes(d))
with open("payload","wb") as f:
    f.write(bytes(d))