[SECCON 2017 Quals] Powerful_Shell

Problem

Crack me.

Solution

A PowerShell script is given:

1
2
3
4
5
6
7
8
$ECCON="";
$ECCON="";
$ECCON+=[char](3783/291);
$ECCON+=[char](6690/669);
$ECCON+=[char](776-740);
# ...
$ECCON+=[char](520-510);
Write-Progress -Completed -Activity "Extracting Script";.([ScriptBlock]::Create($ECCON))

Notice the last command .([ScriptBlock]::Create($ECCON)). It creates and sources a PowerShell script from $ECCON on the fly.1 So we changed it to echo $ECCON > stage2.ps1 to extract the script.

stage2.ps1:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
$ErrorActionPreference = "ContinueSilently"
# A very very long line to draw the SECCON logo

<# Host Check #>
Write-Host -b 00 -f 15 Checking Host... Please wait... -n
Try{
	If ((Get-EventLog -LogName Security | Where EventID -Eq 4624).Length -Lt 1000) {
		Write-Host "This host is too fresh!"
		Exit
	}
}Catch{
	Write-Host "Failed: No admin rights!"
	Exit
}
Write-Host "Check passed"

$keytone=@{'a'=261.63}
$pk='a'
ForEach($k in ('w','s','e','d','f','t','g','y','h','u','j','k')){
	$keytone+=@{$k=$keytone[$pk]*[math]::pow(2,1/12)};$pk=$k
}
Write-Host -b 00 -f 15 "Play the secret melody."

Write-Host -b 15 -f 00 -n '   '
Write-Host -b 00 -f 15 -n '   '
Write-Host -b 15 -f 00 -n '  '
# ...
Write-Host -b 15 -f 00  '    '
Write-Host
$stage1=@();$f="";
While($stage1.length -lt 14){
	$key=(Get-Host).ui.RawUI.ReadKey("NoEcho,IncludeKeyDown")
	$k=[String]$key.Character
	$f+=$k;
	If($keytone.Contains($k)){
		$stage1+=[math]::floor($keytone[$k])
		[console]::beep($keytone[$k],500)
	}
}
$secret=@(440,440,493,440,440,493,440,493,523,493,440,493,440,349)
If($secret.length -eq $stage1.length){
	For ($i=1; $i -le $secret.length; $i++) {
		If($secret[$i] -ne $stage1[$i]){
			Exit
		}
	}
	x "Correct. Move to the next stage."
}
$text=@"
YkwRUxVXQ05DQ1NOE1sVVU4TUxdTThBBFVdDTUwTURVTThMqFldDQUwdUxVRTBNEFVdAQUwRUxtT
TBEzFVdDQU8RUxdTbEwTNxVVQUNOEFEVUUwdQBVXQ0NOE1EWUUwRQRtVQ0FME1EVUU8RThdVTUNM
EVMVUUwRFxdVQUNCE1MXU2JOE0gWV0oxSk1KTEIoExdBSDBOE0MVO0NKTkAoERVDSTFKThNNFUwR
...
ShtME0EVTBFGF0BOE0gVQhNDF0wTVxVBTxFKF0wdQxVOEygXTBE2FxROE10VShZOTBFTF2E=
"@

$plain=@()
$byteString = [System.Convert]::FromBase64String($text)
$xordData = $(for ($i = 0; $i -lt $byteString.length; ) {
	for ($j = 0; $j -lt $f.length; $j++) {
		$plain+=$byteString[$i] -bxor $f[$j]
		$i++
		if ($i -ge $byteString.Length) {
			$j = $f.length
		}
	}
})
iex([System.Text.Encoding]::ASCII.GetString($plain))

Not so pretty because of the customized console… haha

It checks if the host has admin rights, but they aren’t necessary. We can simply remove that part to pass the check.

Wow, there’s a piano! @bayaheuro
Let me take a guess… $secret has the score. 440 Hz is A4, blah blah. Then we get “hhjhhjhjkjhjhf”.

I remember that I transferred at Musashi-Koganei (武蔵小金井駅) when I visited Japan last year

Now, notice the last command again. iex executes the given expression. We changed that line to echo [System.Text.Encoding]::ASCII.GetString($plain) > stage3.ps1.

stage3.ps1:

1
2
3
4
5
${;}=+$();${=}=${;};${+}=++${;};${@}=++${;};${.}=++${;};${[}=++${;};
${]}=++${;};${(}=++${;};${)}=++${;};${&}=++${;};${|}=++${;};
${"}="["+"$(@{})"[${)}]+"$(@{})"["${+}${|}"]+"$(@{})"["${@}${=}"]+"$?"[${+}]+"]";
${;}="".("$(@{})"["${+}${[}"]+"$(@{})"["${+}${(}"]+"$(@{})"[${=}]+"$(@{})"[${[}]+"$?"[${+}]+"$(@{})"[${.}]);
${;}="$(@{})"["${+}${[}"]+"$(@{})"[${[}]+"${;}"["${@}${)}"];"${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${(}${+}+${"}${&}${@}+${"}${+}${=}${+}+${"}${|}${)}+${"}${+}${=}${=}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${[}${]}+${"}${&}${=}+${"}${+}${+}${[}+${"}${+}${+}${+}+${"}${+}${=}${|}+${"}${+}${+}${@}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${|}+${"}${(}${|}+${"}${+}${+}${=}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${+}${+}${[}+${"}${.}${@}+${"}${+}${+}${(}+${"}${+}${=}${[}+${"}${+}${=}${+}+${"}${.}${@}+${"}${+}${+}${@}+${"}${|}${)}+${"}${+}${+}${]}+${"}${+}${+}${]}+${"}${+}${+}${|}+${"}${+}${+}${+}+${"}${+}${+}${[}+${"}${+}${=}${=}+${"}${.}${|}+${"}${+}${.}+${"}${+}${=}+${"}${)}${.}+${"}${+}${=}${@}+${"}${[}${=}+${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${.}${@}+${"}${[}${]}+${"}${+}${=}${+}+${"}${+}${+}${.}+${"}${.}${@}+${"}${.}${|}+${"}${&}${=}+${"}${[}${&}+${"}${+}${+}${|}+${"}${(}${|}+${"}${+}${+}${[}+${"}${.}${(}+${"}${)}${@}+${"}${]}${+}+${"}${[}${|}+${"}${[}${|}+${"}${.}${|}+${"}${[}${+}+${"}${+}${@}${.}+${"}${+}${.}+${"}${+}${=}+${"}${|}+${"}${&}${)}+${"}${+}${+}${[}+${"}${+}${=}${]}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${|}+${"}${)}${+}+${"}${+}${+}${+}+${"}${+}${+}${+}+${"}${+}${=}${=}+${"}${.}${@}+${"}${)}${[}+${"}${+}${+}${+}+${"}${|}${&}+${"}${.}${.}+${"}${.}${|}+${"}${]}${|}+${"}${+}${.}+${"}${+}${=}+${"}${|}+${"}${&}${)}+${"}${+}${+}${[}+${"}${+}${=}${]}+${"}${+}${+}${(}+${"}${+}${=}${+}+${"}${[}${]}+${"}${)}${@}+${"}${+}${+}${+}+${"}${+}${+}${]}+${"}${+}${+}${(}+${"}${.}${@}+${"}${.}${[}+${"}${&}${.}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${+}${@}${.}+${"}${.}${(}+${"}${(}${|}+${"}${(}${)}+${"}${(}${)}+${"}${)}${|}+${"}${)}${&}+${"}${+}${@}${]}+${"}${.}${[}+${"}${+}${.}+${"}${+}${=}+${"}${+}${@}${]}|${;}"|&${;}

Looks so ugly, but we can get a hint from sorting the original script:

1
2
If($host."na`m`e" -ne "C`on`s`o`l`e`H`o`st"){"D`eb`u`g`g`in`g is `pr`o`h`ib`it`e`d";Exit}
If(Test-Path variable:global:psISE){"D`eb`u`g`g`in`g is `pr`o`h`ib`it`e`d";Exit}

Because this check isn’t present in stage3.ps1, we can use PowerShell ISE to debug the script. Open ISE, execute stage3.ps1, type anything, press Ctrl-B and click the break button!

Now we have the flag.