Throw in dnspy and find the following piece of code, which write bytes2
to a file and then execute it:
File.WriteAllBytes(string.Concat(new string[]
{
CS$<>8__locals1.CurrentDirectoryy[0],
"\\",
CS$<>8__locals1.CurrentDirectoryy[1],
"\\",
Environment.UserName,
"\\",
CS$<>8__locals1.CurrentDirectoryy[2],
"\\",
CS$<>8__locals1.CurrentDirectoryy[3],
"\\",
CS$<>8__locals1.CurrentDirectoryy[4],
"\\",
CS$<>8__locals1.fileName,
CS$<>8__locals1.CurrentDirectoryy[5]
}), bytes2);
Process process = new Process();
process.Exited += CS$<>8__locals1.<Main>g__p_Exited|0;
process.StartInfo.FileName = string.Concat(new string[]
{
CS$<>8__locals1.CurrentDirectoryy[0],
"\\",
CS$<>8__locals1.CurrentDirectoryy[1],
"\\",
Environment.UserName,
"\\",
CS$<>8__locals1.CurrentDirectoryy[2],
"\\",
CS$<>8__locals1.CurrentDirectoryy[3],
"\\",
CS$<>8__locals1.CurrentDirectoryy[4],
"\\",
CS$<>8__locals1.fileName,
CS$<>8__locals1.CurrentDirectoryy[5]
});
process.EnableRaisingEvents = true;
process.Start();
Let's set a breakpoint after writing to the file, extract it and throw it into dnspy. We will see exactly the same code, so we will do this procedure again.
Once again we throw the decrypted exe into dnspy and find the nonobfuscated gui code. In the form code, we find the flag comparison algorithm:
private void Button1_Click(object sender, EventArgs e)
{
bool flag = Operators.CompareString(this.TextBox1.Text, "W0wD0tNetREveRsE3asy", false) == 0;
if (flag)
{
Interaction.MsgBox("Bien joué ! Vous pouvez valider le challenge avec ce mot de passe", MsgBoxStyle.OkOnly, null);
}
else
{
Interaction.MsgBox("Bad hacker", MsgBoxStyle.OkOnly, null);
}
}
For decompilation, I used luadec (outputs cleaner code, but more artifacts) and unluac.
Decompile with luadec:
check = function(l_1_0)
start_array = {}
if (string.len)(l_1_0) ~= 23 then
return false
end
for l_1_4 = 1, #l_1_0 do
start_array[l_1_4] = (string.byte)(l_1_0:sub(l_1_4, l_1_4))
end
local l_1_5 = {}
-- DECOMPILER ERROR at PC48: No list found for R1 , SetList fails
-- DECOMPILER ERROR at PC51: Overwrote pending register: R2 in 'AssignReg'
-- DECOMPILER ERROR at PC52: Overwrote pending register: R3 in 'AssignReg'
-- ...
end_array, l_1_5 = l_1_5, {33, 1, 84, -104, -65, 46, -28, -49, 15, 110, -18, 40, -59, -25, 0, 22, -46, -88, -33, -13, 88, -50, 129}
l_1_5 = 1
iterator = l_1_5
while 1 do
l_1_5 = iterator
if l_1_5 <= (string.len)(l_1_0) then
l_1_5 = iterator
l_1_5 = l_1_5 % 2
if l_1_5 == 1 then
l_1_5 = start_array
l_1_5 = l_1_5[iterator]
l_1_5 = l_1_5 + shift_array[iterator]
if l_1_5 ~= end_array[iterator] then
l_1_5 = false
return l_1_5
end
else
l_1_5 = start_array
l_1_5 = l_1_5[iterator]
l_1_5 = l_1_5 - shift_array[iterator]
if l_1_5 ~= end_array[iterator] then
l_1_5 = false
return l_1_5
end
end
l_1_5 = iterator
l_1_5 = l_1_5 + 1
iterator = l_1_5
end
l_1_5 = true
return l_1_5
end
print("Welcome to the Lua Crackme Challenge! Find the correct password to validate the challenge:")
s = (io.read)("*l")
if check(s) then
print("Good Game! You can validate the challenge with this password :-)")
else
print("nope, wrong password!")
end
Sometimes luadec has problems like this:
local l_1_5 = {}
-- DECOMPILER ERROR at PC48: No list found for R1 , SetList fails
-- DECOMPILER ERROR at PC51: Overwrote pending register: R2 in 'AssignReg'
-- DECOMPILER ERROR at PC52: Overwrote pending register: R3 in 'AssignReg'
-- ...
end_array, l_1_5 = l_1_5, {33, 1, 84, -104, -65, 46, -28, -49, 15, 110, -18, 40, -59, -25, 0, 22, -46, -88, -33, -13, 88, -50, 129}
This is where unluac comes to the rescue, which does not compress the creation of the array:
From here we extract the contents of the array and a new name for it - shift_array
Using the knowledge gained, reduce code even more:
function check(input)
local start_array = {}
if string.len(input) ~= 23 then
return false
end
for i = 1, #input do
start_array[i] = (string.byte)(input:sub(i, i)) -- input[i]
end
local end_array = {99, 96, 192, 201, 45, 53, 73, 144, 99, 1, 92, 55, 22, 142, 111, 89, 33, 199, 78, 92, 167, 155, 162}
local shift_array = {33, 1, 84, -104, -65, 46, -28, -49, 15, 110, -18, 40, -59, -25, 0, 22, -46, -88, -33, -13, 88, -50, 129}
local iterator = 1
while 1 do
if iterator<= (string.len)(input) then
if iterator % 2 == 1 then
if start_array[iterator] + shift_array[iterator] ~= end_array[iterator] then
return false
end
else
if start_array[iterator] - shift_array[iterator] ~= end_array[iterator] then
return false
end
end
iterator += 1
end
end
return true
end
print("Welcome to the Lua Crackme Challenge! Find the correct password to validate the challenge:")
s = io.read("*l")
if check(s) then
print("Good Game! You can validate the challenge with this password :-)")
else
print("nope, wrong password!")
end
Remains to solve the check
:
flag = ""
end_array = [99, 96, 192, 201, 45, 53, 73, 144, 99, 1, 92, 55, 22, 142, 111, 89, 33, 199, 78, 92, 167, 155, 162]
shift_array = [33, 1, 84, -104, -65, 46, -28, -49, 15, 110, -18, 40, -59, -25, 0, 22, -46, -88, -33, -13, 88, -50, 129]
for i in range(1,24):
if i%2 == 1:
char = end_array[i-1] - shift_array[i-1]
else:
char = end_array[i-1] + shift_array[i-1]
flag += chr(char)
print(flag)
We have a code of the kind
{$'\x65\x76\x61\x6c',$'\x48\x3d\x70\x72\x69\x6e\x74\x66'}&&$H '\e[30m\n'&& [[ $- =~ x ]] && x=1;
X=$x $'\x62\141's${-:$(()):01} -c "$($'\x65\x63\x68\x6f' -e \\$#${##}$((${##}....#}))"
Let's deobfuscate:
{$\'eval\',$\'H=printf\'}&&$H \'\\e[30m\n\'&& [[ $- =~ x ]] && x=1;
X=$x bash -c "$($\'echo\' -e \\$#${#...}###})"
The script tries to execute sequence of $#{(
via bash -c "echo -e '$#{(...#})'"
. Let's try to decrypt it with echo -e
:
[[ $X ]] && exit;
echo -en "\e[0m";
M=( 42 29978 47 59 44 213 42 219 40 59 57 42 4058 40 59 12 59 21 56 29 42 40 41 43 40 3 41 44 41 42 32 57 56 59 57 2 32 56 37 40 0 43 44 43 43 35 22 41 43 42 40 43 40 43 41 44 41 37 32 2 58 40 56 37 40 43 40 43 40 43 40 43 40 43 40 43 40 43 40 43 40 43 40 43 40 29 42 40 41 44 41 117 32 203 43 40 29 42 40 41 44 41 72 32 203 43 40 29 42 40 41 44 41 30 32 203 43 40 29 42 40 41 44 41 89 32 203 58 40 29 42 40 41 44 41 67 32 203 43 40 29 42 40 41 44 41 117 32 203 43 40 29 42 40 41 44 41 70 32 203 42 42 46 43 46 43 40 29 42 40 41 44 41 26 32 203 44 46 47 32 137 43 40 29 42 40 41 44 41 92 32 203 43 40 29 42 40 41 44 41 25 32 203 43 40 43 40 43 40 43 40 43 40 29 42 40 41 44 41 98 32 203 59 195 56 2 32 42 280 40 59 11 213 2 32 42 297 40 59 11 56 111 68 94 79 88 10 90 75 89 89 93 69 88 78 10 16 10 42 124 109 109 10 16 10 83 69 95 10 73 75 68 10 95 89 79 10 94 66 79 10 90 75 89 89 93 69 88 78 10 94 69 10 92 75 70 67 78 75 94 79 10 16 7 3 42 126 88 83 10 66 75 88 78 79 88 10 68 26 26 72 11 11 11 0 30000 5 17 332 17 0 255 0 0 4 0 324 3 0 42 2 55 0 4 1 43 1 2 3 1 0 4 1 4 6 4 324 10 341 18 );
((P=0x144));
{$'\x65\x76\x61\x6c',$'\x6a\x3d\x27\x70\x72\x69\x6e\x74\x66\x20\x27'}; # {$'eval',$'j='printf ''};
# $j = printf
o="$j %d '";
p="%03o ";
function _0 {((R[M[P+2]]=M[P+1],P+=3));};
function _1 {((R[M[P+1]]++,P+=2));};
function _2 {((R[M[p+1]]=M[P+2],P+=3));};
function _22 {((R[M[p+2]]=R[M[P+1]],P+=3));};
function _3 {((g=M[P+2]+R[M[P+3]],M[g]=R[M[P+1]],P+=4));};
function _55 {((R[M[P+3]]=M[M[P+1]+R[M[P+2]]],P+=4));};
function _6 {((y=R[M[P+1]],z=M[P+2],P+=3));};
function _9 {((y<z))&&((P=M[P+1]))||((P+=2));};
function _10 {((y!=z))&&((P=M[P+1]))||((P+=2));};
function _17 {((R[5]--,M[R[5]]=P+2,P=M[P+1]));};
function _18 {((P=M[R[5]],R[5]++));};
function _14 {((R[5]--,M[R[5]]=M[P+1],P+=2));};
function _15 {((R[5]--,M[R[5]]=R[M[P+1]],P+=2));};
function _16 {((R[M[P+1]]=M[R[5]],R[5]++,P+=2));};
function _40 {(());$j\\$($j$p${M[P+1]});((P+=2));};
function _41 {(());$j\\$($j$p${R[M[P+1]]});((P+=2));};
function _42 {(());{read,-n1,t};R[M[P+1]]=$(${o}${t});((P+=2));};
function _43 {((R[M[P+1]]^=R[M[P+2]],P+=3));};
function _44 {((g=M[P+2]+R[M[P+3]],R[M+1]^=M[g],P+=4));};
function _255 {(());exit;};
while ((1)); do
_${M[P]};
done
Extract the following from the code:
M - mutable bytecode
R[x] - Rx register
P - intruction pointer, rip
y,z,g - special registers
_${M[P]} - vm dispatcher
Let's analyze vm handlers:
function _0 {((R[M[P+2]]=M[P+1],P+=3));}; # R[M[P+2]] = M[P+1]
function _1 {((R[M[P+1]]++,P+=2));}; # R[M[P+1]] += 1
function _2 {((R[M[p+1]]=M[P+2],P+=3));}; # R[M[p+1]] = M[P+2]
function _22 {((R[M[p+2]]=R[M[P+1]],P+=3));}; # R[M[p+2]] = R[M[P+1]]
function _3 {((g=M[P+2]+R[M[P+3]],M[g]=R[M[P+1]],P+=4));}; # g= M[P+2] + R[M[P+3]] ; M[g] = R[M[P+1]]
function _55 {((R[M[P+3]]=M[M[P+1]+R[M[P+2]]],P+=4));}; # R[M[P+3]] = M[ M[P+1] + R[M[P+2]] ]
function _6 {((y=R[M[P+1]],z=M[P+2],P+=3));}; # y= R[M[P+1]]; z = M[P+2]
function _9 {((y<z))&&((P=M[P+1]))||((P+=2));}; # JL # JMP to M[P+1] if y<z
function _10 {((y!=z))&&((P=M[P+1]))||((P+=2));}; # JNE
function _17 {((R[5]--,M[R[5]]=P+2,P=M[P+1]));}; # R[5]-- ; M[R[5]] = P+2; P=M[P+1] # JMP M[P+1]
function _18 {((P=M[R[5]],R[5]++));}; # P=M[R[5]] ; R[5]++ # JMP M[R[5]]
function _14 {((R[5]--,M[R[5]]=M[P+1],P+=2));}; # R[5]-- ; M[R[5]] = M[P+1]
function _15 {((R[5]--,M[R[5]]=R[M[P+1]],P+=2));}; # R[5]-- ; M[R[5]] = R[M[P+1]]
function _16 {((R[M[P+1]]=M[R[5]],R[5]++,P+=2));}; # R[M[P+1]] = M[R[5]]; R[5]++
function _40 {(());$j\\$($j$p${M[P+1]});((P+=2));}; # print( M[P+1] )
function _41 {(());$j\\$($j$p${R[M[P+1]]});((P+=2));}; # print( R[M[P+1]] )
function _42 {(());{read,-n1,t};R[M[P+1]]=$(${o}${t});((P+=2));}; # R[M[P+1]] = getchar()
function _43 {((R[M[P+1]]^=R[M[P+2]],P+=3));}; # R[M[P+1]] ^= R[M[P+2]] # SOME_XOR
function _44 {((g=M[P+2]+R[M[P+3]],R[M+1]^=M[g],P+=4));}; # g = M[P+2] + R[M[P+3] ; R[M+1] ^= M[g] # SOME_XOR
function _255 {(());exit;}; # EXIT_VM
Rewrite in python with the addition of a disassembler
import tty, sys, termios
M = {}
m=[42,29978,47,59,44,213,42,219,40,59,57,42,4058,40,59,12,59,21,56,29,42,40,41,43,40,3,41,44,41,42,32,57,56,59,57,2,32,56,37,40,0,43,44,43,43,35,22,41,43,42,40,43,40,43,41,44,41,37,32,2,58,40,56,37,40,43,40,43,40,43,40,43,40,43,40,43,40,43,40,43,40,43,40,43,40,29,42,40,41,44,41,117,32,203,43,40,29,42,40,41,44,41,72,32,203,43,40,29,42,40,41,44,41,30,32,203,43,40,29,42,40,41,44,41,89,32,203,58,40,29,42,40,41,44,41,67,32,203,43,40,29,42,40,41,44,41,117,32,203,43,40,29,42,40,41,44,41,70,32,203,42,42,46,43,46,43,40,29,42,40,41,44,41,26,32,203,44,46,47,32,137,43,40,29,42,40,41,44,41,92,32,203,43,40,29,42,40,41,44,41,25,32,203,43,40,43,40,43,40,43,40,43,40,29,42,40,41,44,41,98,32,203,59,195,56,2,32,42,280,40,59,11,213,2,32,42,297,40,59,11,56,111,68,94,79,88,10,90,75,89,89,93,69,88,78,10,16,10,42,124,109,109,10,16,10,83,69,95,10,73,75,68,10,95,89,79,10,94,66,79,10,90,75,89,89,93,69,88,78,10,94,69,10,92,75,70,67,78,75,94,79,10,16,7,3,42,126,88,83,10,66,75,88,78,79,88,10,68,26,26,72,11,11,11,0,30000,5,17,332,17,0,255,0,0,4,0,324,3,0,42,2,55,0,4,1,43,1,2,3,1,0,4,1,4,6,4,324,10,341,18,]
for i in range(len(m)):
M[i] = m[i]
P = 0x144
y,z,g =0,0,0
R = 2000*[0]
def getch(): # for 1 char input
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
DEBUG=True
def d(*args):
if DEBUG:
print(str(P)+'>', *args)
while True:
op = M[P]
if op == 255:
d('EXIT')
break
elif op == 0:
R[M[P+2]] = M[P+1]
d(f"R[{M[P+2]}] = {M[P+1]}")
P+=3
elif op == 1:
R[M[P+1]] += 1
d(f"R[{M[P+1]}] += 1")
P+=2
elif op == 2:
R[M[p+1]] = M[P+2]
d(f"R{M[p+1]} = {M[P+2]}")
P+=3
elif op == 22:
R[M[p+2]] = R[M[P+1]]
d(f'R[{M[p+2]}] = R[{M[P+1]}]')
P+=3
elif op == 3:
g= M[P+2] + R[M[P+3]]
d(f'g = {M[P+2]} + R[{M[P+3]}] = {g}')
d(f"M[{g}] = R[{M[P+1]}]")
M[g] = R[M[P+1]]
P+=4
elif op == 55:
R[M[P+3]] = M[ M[P+1] + R[M[P+2]] ]
d(f"R[{M[P+3]}] = M[ {M[P+1]} + R[{M[P+2]}] ] = M[{M[P+1] + R[M[P+2]]}]")
P+=4
elif op == 6:
y = R[M[P+1]]
z = M[P+2]
d(f"y = R[{M[P+1]}]")
d(f"z = {M[P+2]}")
P+=3
elif op == 9:
d(f"JL {M[P+1]} # IF {y} < {z}")
if y < z:
P = M[P+1]
else:
P += 2
elif op == 10:
d(f"JNE {M[P+1]} # IF {y} != {z}")
if y != z:
P = M[P+1]
else:
P += 2
elif op == 17:
d(f"R[5]-= 1")
d(f"M[{R[5]}] = {P+2}")
d(f"P = M[{P+1}]")
R[5] -= 1
M[R[5]] = P+2
P = M[P+1]
elif op == 18:
d(f"P = M[{R[5]}]")
d(f"R[5] += 1")
P = M[R[5]]
R[5] += 1
elif op == 14:
d(f"R[5] -= 1")
d(f"M[{R[5]}] = M[{P+1}]")
R[5] -= 1
M[R[5]] = M[P+1]
P+=2
elif op == 15:
d(f"R[5] -= 1")
d(f"M[{R[5]}] = R[{M[P+1]}]")
R[5] -= 1
M[R[5]] = R[M[P+1]]
P+=2
elif op == 16:
d(f"R[{M[P+1]}] = M[{R[5]}]")
d(f"R[5] += 1")
R[M[P+1]] = M[R[5]]
R[5] += 1
P+=2
elif op == 40:
d(f"print M[{P+1}]")
print(chr(M[P+1]),end='')
P+=2
elif op == 41:
d(f"print R[{M[P+1]}]")
print(chr(R[M[P+1]]),end='')
P+=2
elif op == 42:
inp_chr = getch()
R[M[P+1]] = ord(inp_chr)
d(f"R[{M[P+1]}] = ord(\"{inp_chr}\") = {ord(inp_chr)}")
P+=2
elif op == 43:
d(f"R[{M[P+1]}] ^= R[{M[P+2]}]")
R[M[P+1]] ^= R[M[P+2]]
P += 3
elif op == 44:
d(f"g = M[{P+2}] + R[{M[P+3]}] ")
d(f"R[{M+1}] ^= M[{g}]")
g = M[P+2] + R[M[P+3]]
R[M+1] ^= M[g]
P += 4
else:
print(f'ERROR at P = {P}')
break
Let's notice a some things:
String length is 15:
The string is compared character by character. On a mismatch, a JMP 225
occurs with the output Try harder noob
. Accordingly, we can make a string of length 15 and fit each character to the one that will be compared at JNE 22 # IF inp_chr != need_chr
Let's generate a string from which we will start when brutforce
st = "".join(map(chr, range(60, 75))) # <=>?@ABCDE_GHIJ
And we will edit the handler of the getchar instruction so that it is more convenient to brutforce the string
st = input()
i=-1
while True:
...
elif op == 42:
# inp_chr = getch()
i+=1
inp_chr = st[i]
...
...
We look at what the character from our string is compared with and change it to the desired one. Going through character by character, we get the coveted result:
I just used angr lol
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up