2024年“羊城杯”粤港澳大湾区网络安全大赛个人WriteUp
咱羊城杯终于不是零分战士了,想我22年打的时候,一题搞不出来,零分告别比赛(难过的小曲——)
本次我是单人成队,因为打这个比赛的时候还没有入学,就纯靠自己的稀泥基础打
数据安全 (Data Security)
data-analy1
小王在处理个人信息时,不小心把数据给逐行打乱了,请你帮助他进行数据的整理恢复。具体示例可参考附件中“示例”文件夹所示。最终将整理恢复后的数据文件(文件格式 csv,文件编码 utf-8)上传至检验平台,检验达标即可拿到flag。
附件下载下来很多东西,特别是提供的csv里面数据非常凌乱(注:本题的数据均为随机生成)
从附件中的pdf,我们可以得到以下信息
- 用户名:由数字和字母组成。
- 密码:密码的 hash 值为 32 位小写 MD5 值。
- 姓名:由全中文字符组成。
- 性别:只能为“男”或“女”,与身份证号中的性别位对应。
- 出生日期:由 8 位数字组成,与身份证号中的出生日期码一致。
- 身份证号:18 位,分为地址码(6 位)、出生日期码(8 位)、顺序码(3 位)和校验码(1 位)。
- 性别:倒数第二位表示性别,奇数为男性,偶数为女性。
- 地址码:随机生成,不与现实中的身份证号关联。
- 校验码:通过前 17 位的加权和计算得到,系数分别为 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2,余数对应的校验码通过查表得出。
- 手机号码:11 位数字字符串,前 3 位号段是虚假号段,限定在指定集合中,734, 735, 736, 737, 738, 739, 747, 748, 750, 751, 752, 757, 758, 759, 772, 778, 782, 783, 784, 787, 788, 795, 798, 730, 731, 732, 740, 745, 746, 755, 756, 766, 767, 771, 775, 776, 785, 786, 796, 733, 749, 753, 773, 774, 777, 780, 781, 789, 790, 791, 793, 799
所以我们可以写一个Python脚本来解决这个数据清洗问题
1 | import re |
跑一遍就可以得到符合题目要求的csv文件
然后交给平台就有flag了
data-analy2
某公司在统计内部员工个人信息,不过上网流量数据没有进行任何加密处理,现在流量已经被黑客获取。现在请你分析流量,统计出该公司内部员工的个人信息,由于某些员工没认真填写,导致某些数据是不符合数据规范的,因此需要进行数据清洗。数据规范文档参考附件中“个人信息数据规范文档.pdf”。最终将清洗后的垃圾数据(文件格式为 csv,文件编码为 utf-8)上传至检验平台,检验达标即可拿到 flag。
附件下载下来有一个PDF和一个pcapng数据流文件,打开数据流文件稍微翻看就能够发现,“员工”提交数据的时候,是使用的HTTP协议,且发送的数据为json
所以用wireshark过滤表达式http && json
就可以过滤出所有提交用的数据流
确定过滤条件后,使用tshark进行数据提取
1 | $ tshark -r data.pcapng -Y "http" -T fields -e http.request.uri -e http.response.code -e http.file_data -E separator=, -E quote=d > output.csv |
提取出来的数据非常丑
不要紧,我们在脚本里面定义一个函数进行初次过滤
1 | def firstFilter(data) -> list: |
过滤后可以得到很漂亮的jsonl内容
而根据个人信息数据规范文档文件,我们可以得到这次的数据有以下要求
用户名 (username):只能由数字和字母组成。
姓名 (name):只能由全中文字符组成。
性别 (sex):只能为“男”或“女”,并与身份证号中代表性别的位对应。
出生日期 (birth):由 8 位数字组成,与身份证号中的出生日期码一致。
身份证号 (idcard):
- 18 位,包含:6 位地址码、8 位出生日期码、3 位顺序码和 1 位校验码。
- 性别:倒数第二位表示性别,奇数为男性,偶数为女性。
- 地址码:随机生成,不与现实中的身份证号关联。
- 校验码计算:通过前 17 位数字乘以指定系数、求和、除以 11 得余数,再通过查表得到校验码。
手机号码 (phone):
- 11 位数字字符串,前 3 位号段为虚假号段,限定在指定集合中。
上传文件规范:
清洗后的数据需保存为 UTF-8 编码的 CSV 文件。
示例文件内容需符合规范,上传到校验平台进行验证,验证通过后可获取 flag。
所以我们需要的是不符合条件的数据,我们先根据条件写好判断函数
1 | def isNameValid(name: str) -> bool: # 判断名字是否符合纯中文 |
写好这些函数后再整合到第二层过滤器里面
1 | def secondFilter(line) -> bool: |
最后根据是否合法写出到文件
1 | if isValid: |
最终整个脚本就变成了这样
1 | import csv |
跑完就会生成两个csv,我们交invalid.csv上去就得到flag了
data-analy3
某公司在内部做了一个收集个人信息的简易网站供员工进行登记,但网站管理员在整理时误删了数据库里的数据,现在请你根据日志,还原出所有用户的个人信息,个人信息包括【username、password、name、idcard、phone】。现在请你参考附件中“个人信息数据规范文档.pdf”所示对整理出的个人信息进行数据脱敏,脱敏后保存到 csv 文件中(文件编码为 utf-8),并将其上传至检验平台,检验达标即可拿到 flag。
文件下载下来是阿帕奇的日志和错误日志,日志里面什么信息都没有,只有错误日志还有那么点用
随便翻一翻可以看到用户输入的数据为明文,所以第一步直接拿出来
后面发现题目告诉我还有密码,所以又去翻看error.log,发现有一长串的字符,解码出来是您的信息录入成功!您的密码为
,
然后我再查了一下,查username=
和您的信息录入成功!您的密码为
发现数量对不上,username的数量比密码多,所以又对第一次过滤进行了修改,只有当username后面紧接着password才认为是一对
1 | def firstFilter(data: list): |
第一次提取出内容后,就要对内容合法性进行验证,按照个人信息数据规范文档,跟第二题的要求一致,所以直接把第二题的代码拿过来用
1 | def secondFilter(username, name, idcard, phone) -> bool: |
接着第三次清洗,按照文档要求的脱敏规则进行脱敏
username:若只有两个字符则只对最后⼀位使⽤
*
号代替,否则只保留第⼀位和最后⼀位字符,其余都⽤*
号代替,例如ab
脱敏后就是a*
,abcde
脱敏后就是a***e
password: 对其做 md5 加密处理,例如
123456
进⾏处理后为e10adc3949ba59abbe56e057f20f883e
name:⼆字姓名对最后⼀位使⽤
*
号代替,三字即以上姓名除第⼀位和最后⼀位以外都使⽤*
号代替,例如张三
脱敏后就是张*
,王不⼆
脱敏后就是王*⼆
idcard:只保留年份,其余都使⽤
*
号代替,例如172865199108200356
脱敏后就是******1991********
phone:对 4-7 位的地区编码使⽤
*
号代替,例如74580417166
脱敏后就是745****7166
于是就有了脱敏函数
1 | def desensitize(username, name, idcard, phone, password): |
最后上述所有的代码组合起来,就有了最终的一键傻瓜式脚本
1 | import csv |
把生成的output.csv
交到靶机上就有了
Web
Lyrics For You(未解出)
I have wrote some lyrics for you……
开启靶机,连接靶机发现三个链接
随便点击一个,发现url中有?lyrics=Rain.txt
,尝试后发现存在目录穿越漏洞
Wappalyzer提示用flask开的服务器,所以猜测用了open()
函数,尝试传入lyrics=/proc/self/cmdline
可以得知Python源文件的位置在/usr/etc/app/app.py
,传入lyrics=/usr/etc/app/app.py
得到源码
app.py
1 | # http://139.155.126.78:34094/lyrics?lyrics=/usr/etc/app/app.py |
从此源码还可以看到从config
导入了secret_key
,还有从当前目录导入了cookie
(cookie不是pypi上的包,故猜测是本地文件),使用此方法一起读取出来
config/secret_key.py
1 | # http://139.155.126.78:34094/lyrics?lyrics=/usr/etc/app/config/secret_key.py |
cookie.py
1 | # http://139.155.126.78:34094/lyrics?lyrics=/usr/etc/app/cookie.py |
发现在cookie.py
里面存在pickle.loads
,猜测为pickle
的反序列化漏洞,尝试使用__reduce__
魔术方法,但是后来发现在waf
函数中有过滤
1 | def Waf(data): |
而__reduce__
序列化后的方法标识为R
,会直接踩第一个条件,所以不可行……后来一直构建不出能够执行命令的payload,故作罢
下面附上其他静态资源
templates/index.html
1 |
|
templates/login.html
1 |
|
templates/user.html
1 |
|
templates/admin.html
1 |
|
static/style.css
1 | body { |
tomtom2(未解出)
Where is my tomcat password? /myapp
题目告诉我们路径为/myapp
,我们访问后,可以看到几个链接
选择/env
可以看到当前的环境变量
1 | JAVA_HOME=/usr/local/openjdk-8 |
点到/read
可以读取文件,但是限定了.xml
后缀名,我们传入/myapp/read?filename=conf/server.xml
,读取tomcat配置
读取了以后,在网络选项卡对应请求的响应中
得到server.xml
内容如下(删除了部分注释)
1 |
|
从配置文件中,我们可以发现下面的内容
1 | <Resource name="UserDatabase" auth="Container" |
说明了几个信息:
- 存在文件
conf/tomcat-users.xml
,作为用户的数据库使用 - 网站应用目录为
webapps
文件夹,仅监听localhost
地址 - 存在logs文件夹,log以
localhost_access_log.txt
文件名保存
传入/myapp/read?filename=conf/tomcat-users.xml
,可以读取到用户数据库表
1 |
|
得知用户admin
的密码为This_is_my_favorite_passwd
,角色为manager-gui
接着访问/myapp/login.html
,用刚刚得到的信息进行登录,登录后是一个图床程序
先试试随便上传个图片,看看请求
但是提示上传失败,也就是我们无法访问我们上传的图片,但是链接里面告诉我们上传的文件夹为/uploads
尝试不传入图片,直接发起上传请求,发现报错
没看出啥,找找别的路子,回到/read
路径,尝试传入非.xml
路径,发现有过滤
尝试读取conf/context.xml
,发现WEB-INF
目录下还有几个文件,但是实测无法读取
摸索着发现,上传的文件后缀名改成.xml
就能够上传上去
尝试修改一下上传的文件夹,发现上传文件夹可控
然后尝试利用上传/opt/tomcat/webapps/myapp/WEB-INF/web.xml
来暴露目录,web.xml
内容如下
1 |
|
修改Burpsuite参数,让文件传上去
但是我访问/myapp/files/*
还是访问不了文件,不知道是哪里出了问题