参与哔哩哔哩安全挑战赛的一点心得
ScreenShot_2020-10-24_21-36-47.png晚上闲着没事儿打开手机B站，登录界面画风突变，什么？！程序员节。好吧，确实有些孤陋寡闻，10.24原来是程序员节。尤其今年，2020-1024=996，哈哈，巧合罢了。总之瞎点就进了题目页，开始我真以为又是什么弱智玩意儿，把UI做的很漂亮，内容又没什么营养，B站活动老套路了。然而，这一次。事情并不这么简单。B站这次仿照夺旗战的规则设置了10个flag，要用各种办法找到flag才能加分。第一题的链接直接给到了服务器ip，答案需要看网页源码才能发现。到这里我其实已经隐约觉得不会那么小儿科，F12对于程序员是日常操作，但也足以过滤大批小白。第二题的提示是需要特殊设备才能看到，我首先想到的是手机，于是接着再F12里使用手机浏览，无果，再试着改成Linux，Mac，依然没有动静。这时候我才想起来去看页面上那么大的一排字。ScreenShot_2020-10-24_21-46-30.png需要时用这个并不存在的浏览器访问，好家伙，我直接设置了一个新的浏览器标识。果然，一刷新，答案就显示出来了。ScreenShot_2020-10-24_21-48-26.png第三题算是歪打正着。ScreenShot_2020-10-24_21-49-15.png不唬你，我真就只试了两遍。一看，啊，管理员の用户名，那直接用户名写个admin，密码也写admin，结果错了。再把密码改成bilibili，靠，这就是真正的秘密。社工直觉还是可以的。ScreenShot_2020-10-24_21-53-29.png第四题已经是第二天起来做的了，讲真这一题我弄了很久，甚至一度想放弃。打开网页的一分钟里我就想到了解法，却败在了工具上。网页的cookie中有一段很像md5的数值，磁链见多了的我一眼就看出来了。理论MD5是没法逆运算的，但毕竟md5值对应的字符是唯一的，所以常用的还是可以查到。这个查出来是user，所以我直接想到替换乘superadmin，admin，administrator这些。然而试了几个插件，都不行，甚至在群里看到了正解是Administrator的MD5，依然无效。下午就折腾单片机去了。晚上越想越不爽，还是要解出来，继续找工具。Burp是好，但是手头Java版本的问题装不上去破解的。无意间再Github找到一个八年前的Chrome插件，有效。ScreenShot_2020-10-24_22-02-15.png第五个题需要写个小脚本，手动遍历可太累了。ScreenShot_2020-10-24_22-04-15.png杯具的第六题，已经被橄榄了。网上看到第六题能做出第十题的结果，果然不错，用npp打开图片就能看到藏着的flag。ScreenShot_2020-10-24_22-26-22.png顺便找到了第八题的解法，现场装了个redis弄出来了，到这里已经涉及我的知识盲区。。现在总分已有70，第七题和第九题目前还没有消息，第六题如果服务器恢复了的话看看有没有办法弄出来。看到很多搞竞赛的大佬表示这不是真正的ctf，更像是解谜。确实哈，尤其你看破解密码那个。前4题能能解还是受益于我平时经常翻源码来获取网页上的一些资源，还有做网页测试，爬虫这些。第五题让我想到之前下载m3u8流的视频，他是一整串，当时有写过python去拉整个下载表。这次换成了网址遍历，开始的时候后我没加cookie，直接406，当场学了一手怎么把cookie包进去。说来惭愧，当时用B站挂机脚本的时候就在想这个cookie是怎么放进去的，然而真正学会还是在今天。整点这种活动挺好的，自己动手找到答案真的成就感满满。图片来源：哔哩哔哩
ScreenShot_2020-10-17_03-26-44.png玩过GTA线上的小伙伴对着页面一定不陌生，这是GTAOL里CEO办公室电脑登录的界面。GTA线上相比线下在地图上并没有太多变化，但没记错的话就是从CEO任务的出现开始，某一些建筑也被赋予了功能。这次做的是记账程序的登陆界面，之前光顾着改程序本身的代码了，这个登录界面稍微改了个背景就放着没管了。但回过头来想想，人家打开你这网站，先看到的就是登陆和注册界面，要是这里看上去很Low的话，人家连继续注册的欲望都没有了。这次做的界面几乎做到了1：1还原，不过，左下角加上了登录和注册的切换按钮，这个在GTA中是不存在的。注册界面我就照着这个风格自由发挥了一下。ScreenShot_2020-10-17_03-27-05.png增加了用户协议这个选项，如果以后要推广这个网站的话用户协议也是避免出现纠纷的重要元素。ScreenShot_2020-10-17_03-27-14.png惭愧地说，我直到前不久才真正的了解了Javascript，过去的我只是会复制粘贴人家写好的代码，也不知道具体是啥原理。漫展那会儿，Google的大佬过来给我系统化的把这些语言，框架，对应的软件都理了一下。我曾经甚至以为Java和Javascript是一个东西。。。目前我的理解是，JS在网页中是静态和动态之间的产物。他不像PHP那么动，也不像HTML那么静。稍微看了几个案例，照猫画虎的做了这个确认用户协议和Submit按钮的联动。在打勾之前，按钮是灰色的，且disable。打勾之后，按钮上色，且enable。除此之外，对于我的主页，我也常使用JS配合Json实现了部分的多语言适配。至于为什么是部分，目前能做的是在标签中的文字可以跟随选择变换语言，但是title暂时不行。还得继续研究研究。
按照之前的计划，周五去杜塞尔多夫接到了从慕尼黑来的Armor。他毕业后在微软实习，目前入职德国谷歌，是实实在在的编程大佬。周末我们参加了Dokomi漫展，本来应该是五月底的漫展，因为疫情，硬是给拖到了九月底。我们两个都是老二次元了，展子上拍coser什么的，轻车熟路。他在我家过夜，虽说离场馆路途遥远，回到家供扯淡的时间寥寥无几，但我还是见缝插针的向他请教一些编程方面的问题。聊到Github，他提到了版本管理，这个对我来说还比较陌生的概念。Github可以培养很好的版本管理意识，这也是许多大的代码公司非常看重的能力。我之前折腾Github主要是为了弄博客，当时有个master分支，本地的改动写个commit再push上去。当时只是比较好奇出了master以外，其他branch的作用是什么，我当时只是把他们当作项目底下的其他文件夹（硬要这么说也没错）但经过大佬指点，我逐渐明白了branch的真正用途。越是大的项目，合理的版本管理就越是重要。举个例子，现在需要给一个网站写一个新feature,目前这个网站已经上线。这个新的feature需要和网页上其他的部分做联动，如果我直接修改现有的网页文件，用户很可能看到未开发完毕的页面，体验很不好。此时一个解决办法就是把整个网站clone到本地，开发测试完成后再deploy到线上。不过，要是我同时需要开发几个不同的feature呢，或者说，多人合作去做一个项目。本地的clone的方法不再好用，要对付这样的情况，就得用Github这样的有版本管理功能的仓库了。比如master主分支可以作为用户看到的现有版本，在这个分支下，我可以创建develop分支，或者test分支，再往下，每一个不同的feature都可以再进行展开。画成图形，这不就正像一颗枝叶茂密的树么。用branch来形容再形象不过。每一个branch写完之后，确认没问题就可以merge到master，这样用户就能使用新的功能了。用户和开发两边都会有不错的体验。为了实践，我把我的homepage和记账项目上传到了github，并尝试了最基本的分支操作。github仓库页面这里我已经建好了仓库，到这里和以前没有不同。branch使用桌面应用或者网页端再Master下创建了dev分支，现在把软件中切换到dev分支。这里稍微插两句。为了真正的让分支起到作用，我花了点时间，折腾了本地网页服务器，还有github到remote server的同步。nas本地服务器直接使用Synology的webstation，源文件夹设置成Github仓库本地文件夹。本地的代码更新将实时反馈到NAS上的网页。homepage先测试一下本地的访问，一切ok，可以无视chrome提示的网页不安全，本地的证书懒得搞了。code稍微修改一下代码，比如我想把这里的2020改成2017-2020。code修改完成 保存page本地页面瞬间更新githubgithub软件这边也检测到了更新，我们再次确认选择的是dev分支，给commit写个description，不写也可以，然后push。pushgithub在github网页版上看结果，目前master分区没有任何变化。dev在dev分支下，可以看到index的改动。如果是多人合作的项目，其他开发者也可以看到这条改动并进行review或者优化。online线上的版本还是改动之前的。现在假设代码没有问题，我们决定deploy刚才的改动。pull request点击pull request，可以看到箭头从dev指向master。这里也可以写点comment来记录。pull request现在已经提交了merge申请，这一步操作个人开发者来说意义不大，如果是多人团队，可以设置不同的权限。比如这里，权限较低的开发成员只能提交request，能否上线还需要admin进行确认。github自动进行了校验，返回了一个绿勾。如果代码写的有问题这里可能提示conflict，那么就还是得打回去继续改代码。我们确认后点击merge pull requestmerge允许merge并关闭了这个requestgithub这边master已经开始了更新deploy因为目前网页服务不是跑在github上，而是单独的VPS，这边还需要通过github action的脚本同步。deploygithub action会把新文件通过rsync上传到server上。complete上传完成。github回到github仓库页面，master分区这边也出现了个绿勾，说明merge已经完成。home再次刷新线上的homepage，更新的内容已经出现。到这里，整个演示完成，回顾一下版本管理的好处。1.新的开发不会影响已有线上项目的运行。2.每个feature的开发都相对独立，管理更方便。3.多人团队开发，权限分明，敏感代码不易泄露。4.merge到主分之前，review更轻松，只需检查单个request中的内容。5.使用类似Github的代码托管平台，必要时可版本回溯。
下载整份代码code.rar传感器到SQL//引入各种库 #include <ESP8266WiFi.h> #include <SPI.h> #include <Wire.h> #include <MySQL_Connection.h> #include <MySQL_Cursor.h> #include <Adafruit_Sensor.h> #include <Adafruit_BME280.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> //定义屏幕宽度和高度 #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 //定义屏幕的连线GPIO号 #define OLED_MOSI 13 #define OLED_CLK 12 #define OLED_DC 3 #define OLED_CS 1 #define OLED_RESET 15 #define SEALEVELPRESSURE_HPA (1013.25) //定义QNH海压 方便计算高度 //输入参数到库 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS); Adafruit_BME280 bme; IPAddress server_addr(xxxxxxx); //服务器ip char user[] = "8266"; //MySQL 用户名 char password[] = "xxxxx"; //MySQL 密码 char INSERT_SQL[] = "INSERT INTO `8266`.`bmedata` (Temp, Humi, Pres, Device) VALUES (%s, %s, %s, %d)"; char Temp[15];//定义几个字符串 分别是 温度 湿度 气压 char Humi[15]; char Pres[15]; char deviceid[] = "fkun06"; //设备名 方便管理 char ssid[] = "F_KUN"; //WiFi名称 char pass[] = "xxxxx"; //WiFi密码 WiFiClient client; MySQL_Connection conn((Client *)&client); //初始化 void setup() { Serial.begin(9600); //串口波特率 Serial.println(F("Serial Start!")); //串口输出一些内容测试 display.begin(SSD1306_SWITCHCAPVCC); //屏幕相关 bme.begin(); //传感器相关 display.clearDisplay(); //清屏 display.setTextSize(2); //字体大小 display.setTextColor(WHITE); //字体颜色 display.setCursor(30, 20); //位置 display.print(F("Start!")); //屏幕输出一些文字 display.display(); //屏幕输出 delay(1000); //延迟1秒 display.clearDisplay(); //清屏 display.setTextSize(1); //字体大小 display.setTextColor(WHITE); //字体颜色 display.setCursor(0, 0); //位置 display.print(F("WiFi Connecting")); //屏幕输出一些文字 display.display(); Serial.print(F("WiFi Connecting")); //串口输出一些文字 WiFi.mode(WIFI_STA); WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { // 如果WiFi没有连接，一直循环打印点 delay(500); Serial.print("."); display.print("."); display.display(); } display.clearDisplay(); display.setTextSize(1); //字体大小 display.setTextColor(WHITE); //字体颜色 display.setCursor(0, 0); //位置 display.print(F("WiFi Connected !!!")); //屏幕输出一些文字 Serial.print(F("WiFi Connected")); //串口输出一些文字 Serial.printf("\nConnected to %s", ssid); Serial.print("\nLocal IP address: "); Serial.print(WiFi.localIP()); // 打印开发板的IP地址 Serial.print("\nDevice MAC:"); Serial.println(WiFi.macAddress()); display.display(); delay(1000); } void getdata() { float h = bme.readHumidity(); //定义浮点数 h等于传感器获取到的湿度数据 下类同 float t = bme.readTemperature(); float p = (bme.readPressure() / 100.0F); dtostrf(h, 1, 2, Humi); //将上面的浮点数保留两位小数输出到最开始定义的几个字符串名下 dtostrf(t, 1, 2, Temp); dtostrf(p, 1, 2, Pres); } void dataprint() { Serial.print(F("Humidity: ")); //串口内输出传感器获取的参数 Serial.print(Humi); Serial.print(F("% Temperature: ")); Serial.print(Temp); Serial.print(F("°C ")); Serial.print("Temperature = "); Serial.print(bme.readTemperature()); Serial.println(" *C"); Serial.print("Pressure = "); Serial.print(bme.readPressure() / 100.0F); Serial.println(" hPa"); Serial.print("Approx. Altitude = "); Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA)); Serial.println(" m"); Serial.print("Humidity = "); Serial.print(bme.readHumidity()); Serial.println(" %"); Serial.println(); display.setTextSize(1); //屏幕输出传感器参数 display.setTextColor(WHITE); display.setCursor(0, 0); display.clearDisplay(); display.print(F("Humidity: ")); display.print(Humi); display.print(F(" %")); display.setCursor(0, 16); display.print(F("Temperature: ")); display.print(Temp); display.print(F(" *C")); display.setCursor(0, 32); display.print(F("Pressure: ")); display.print(Pres); display.print(F(" hPa")); display.setCursor(0, 48); display.print(F("Altitude: ")); display.print(bme.readAltitude(SEALEVELPRESSURE_HPA)); display.print(F(" m")); display.display(); } void sqldata() { char query[128]; sprintf(query, INSERT_SQL, Temp, Humi, Pres, deviceid); //转化数据格式匹配数据表 Serial.print("Ready to Push: "); Serial.println(query); MySQL_Cursor *cur_mem = new MySQL_Cursor(&conn); cur_mem->execute(query); delete cur_mem; } void pushdata(){ if (WiFi.status() == WL_CONNECTED) { if (conn.connect(server_addr, 3306, user, password)) { // 连接数据库 Serial.print("Start Pushing Data!"); sqldata(); display.clearDisplay(); display.setTextSize(1); //屏幕输出传感器参数 display.setTextColor(WHITE); display.setCursor(15, 30); display.print(F("Data Pushed !")); display.display(); Serial.print("Data Pushed !"); } else { Serial.print("Connection to Server FAILED !!!"); } } } //循环 void loop() { int i = 0; while(i<=10){ //计数小于等于10保持循环 delay(2000); //等待2秒 getdata(); dataprint(); i++; } pushdata(); } PHP文件<?php header("content-type:text/json;charset=utf-8"); $con = mysqli_connect("localhost", "8266", "xxxx"); if (!$con) { die('Could not connect database: ' . mysqli_error()); } $db_selected = mysqli_select_db($con,"8266"); if (!$db_selected) { die ("Can\'t use yxz : " . mysqli_error()); } mysqli_query($con,"SET CHARACTER SET utf8"); $sql = mysqli_query($con,"SELECT * FROM `bmedata` "); $arr = array(); while($row = mysqli_fetch_array($sql)) { $count=count($row);//不能在循环语句中，由于每次删除 row数组长度都减小 for($i=0;$i<$count;$i++){ unset($row[$i]);//删除冗余数据 } array_push($arr,$row); } echo json_encode($arr,JSON_UNESCAPED_UNICODE); mysqli_close($con); ?> HTML页面 温度为例 FKUN SMARTHOME VIEW
2.png大晚上的突然频繁的收到评论，ip指向毛子。暂时没功夫仔细研究，反手开了评论审核。以前有正常评论过的用户，继续用老的邮箱和用户名就可以发送。新的用户第一则评论需要我手动通过，之后的也就可以直接发了。
今天基本研究清楚数据在多个表之间的传输方法，比如在帐目中有个支付方式，我们首先希望这个支付方式可以不用手写而是直接用菜单进行选择。但针对不同的用户，支付方式可能多种多样，只给出固定的option显然不是最佳方案。我们可以单独建立一个表，其中包含支付方式名称，对应的序号，以及创建者的用户id。用户可以在管理页自行添加需要的支付方式，输入名称后，系统会自动分配一个对应的序号。在之后用户记账过程中，选框会根据当前的用户id对表内的诸多支付方式进行筛选，选出该用户所创建的，并给予显示。用户选择合适的项目后，option返回该选项对应的序号到主账目表中。用户查账时，对应的序号会重新在含有支付方式的表中寻找对应名称显示。此法优点有三，1用户可自定义内容，2用户之间不会干扰，3重命名，移动，删除等操作被简化，修改不需要改动主账目文件。ScreenShot_2020-01-09_21-02-05.png账目的颜色稍微做出了调整，支出为红色，收入为绿色，特殊消费为蓝色。特殊消费包括偶然大额消费和固定消费。账目部分基本完成，后续有空再慢慢完善账目导出导入的功能。网页UI目前让我头很大，但暂时只能忍忍，接下来大的改动还很多，基本定型了再美化。下一步是再现数据分析，计算平均消费，最低生活消费，制作月消费，年消费折线图等。最终需要引入合适的算法进行消费预估，目标是要预测的比excel目前做的准确。