In a bug bounty campaign, a non-standard command injection vulnerability was discovered. I could not inject new commands with command separators and command substitutions for the system to execute. Therefore, to some extent, it is not a command injection vulnerability. ; But I can use the target environment to make the payload reach the system command line, read and write files, and execute new commands. Therefore, it is like command injection. This kind of utilization method of injecting commands indirectly through the environment is rarely seen in articles discussing command injection.
0x00 cool fun wargame
Due to the non-disclosure agreement, I cannot disclose the details of the original vulnerability, but I am more aware of “talk is cheap, show me the code”. So I found a wargame. Compared with the bounty bug, it not only embodies the same essence, but also has some restrictions, so it becomes more interesting and challenging.
I wrote the wargame source code into CMDi_lab/escaping_quotation/index.php, the core is as follows:
Take a quick look at the source code. First, use the GET method to get the two parameters f1 and f2; then, filter f1 and f2 with the same regularity, including filtering quotation marks to prevent the use of the environment to write webshell, filtering common command separators (;), command substitutions ($()) Prevent new commands from being injected, filter common commands (ls, cat) and prohibit basic operations; then, use quotation marks to build another fortification, so that all input is in quotation marks, so that the command line metacharacters are invalid; finally, use f1, f2 as command parameters Execute the system command file.
how about it! It seems to be a very complete defense system. Try to visit normally first:
The server executes the file command to correctly identify the type of directory and file. Try to submit the command separator (;) and command replacement (“):
Since the regular expression on the server side matches; and “, the submitted file name is set to be empty, so the file prompts that the relevant file cannot be found.
I will look for a breakthrough from three levels: attack regularity, attack quotation marks, and attack command lines.
When reviewing regular expressions in the command injection scenario, I am used to focusing on four aspects: whether to use the multi-line pattern modifier (/foo/m), whether to omit the newline character at the end of the matched object (/^\d+$/), and whether to allow Blank character (\s), whether the backslash match pattern (/\\/) is incorrectly written.
Use multi-line mode modifiers. When the multiline pattern is used to match the characters that you want to allow, there will be logic problems. For example, the following code:
I originally wanted to allow only IP addresses in the format xx.xx.xx.xx. Due to the multi-line mode, as long as a certain line in the input satisfies the conditions, then I can enter multiple lines with line breaks, and the first line meets the requirement 127.0. 0.1. Arbitrary content in the second line, which can easily bypass regular restrictions:
This wargame does not use multi-line mode, so this problem does not exist.
Missing a newline at the end of the matched object. Some patterns ignore the newline character at the end of the string when matching, and the newline character itself is a valid directory separator, which will cause new commands to be injected. The following code:
The original intention is to filter out all characters except letters and numbers in the input. Try to enter a newline character:
Sure enough, it was filtered. However, if you put the newline character at the end of the string, the regular rule will not match:
This is fun, and happily injecting new commands:
This wargame failed to filter out newline characters, but not because of the above reasons.
White space characters are allowed. Blank characters include four spaces, line breaks, horizontal tabs, and vertical tabs. The newline characters injected by the command are also included. Code:
Originally, only letters, numbers, spaces and other characters were allowed, but the newline character was omitted, leading to a command injection vulnerability:
This wargame failed to filter out newline characters, but not because of the above reasons.
Wrongly written backslash matching pattern. The regular expression itself is a string, not directly passed to the regular engine, but the string is processed by the language before being passed to the regular engine. I hope to match the backslash (\) and think about the process in reverse. Since the backslash is a special character in the regular engine, \\ can make the regular engine correctly recognize the backslash \; before the regular engine, \\ passes In the string processing of the language, since the backslash is also a special character in the string, one \ must be represented by \\ and two \ must be represented by \\\\. So, whenever you use regular expressions to match slashes, you must use \\\\. This is a common problem of scripting languages that don’t have the native string feature (r), and there is a slight twist. such as:
Remember that wargame also filtered the backslashes. Looking back, wow, it was indeed misused:
OK, in the regular part, I found the backslash due to miswriting the matching pattern. How to use it? do not know.
0x02 quote escape
Then I will consider lines 25 and 26. The purpose of these two lines is very clear. The input string is wrapped in quotation marks to prevent the possibility of passing some special characters to the command line environment due to the lack of regular filtering. The idea is correct, but the effect is not satisfactory.
Once the payload enters the quotation marks, it will degenerate into a normal string, so it is not lethal. The only exception is the command substitution character (“ or $()). Unfortunately, the command substitution character is strictly guarded by the regular rules and cannot reach 25, 26 lines. So, subconsciously thought that the quotes escape.
The purpose of quotation mark escape is to make the input jump outside the quotation mark and restore the identity of the special character, instead of being bound by the quotation mark, it is just an ordinary character. I often use two methods, one is closing and the other is escaping.
Close tactics to escape the quotes. Add a quotation mark to the input and pair it with the left quotation mark to close it naturally. Then, malicious characters can appear in the input. Finally, add a quotation mark to the input and pair it with the closing quotation mark, or enter a comment to ignore the closing quotation mark. such as:
All my input can only be left in quotation marks, causing the command separator to not be correctly recognized by the command line:
I added two quotation marks (②, ③) in the input, so that it just closes with the quotation marks in the code (① and ②, ③ and ④), so my other input characters (;id;) can appear between the quotation marks Outside, the quotes are successfully escaped:
Use escaping to escape the quotes. The quotation mark itself is also a special character. If there is a way to make it a normal character, then other special characters entered can be correctly recognized by the command line. Backslash can do it! The following code:
Inputs containing malicious characters are restricted to quotation marks:
Assuming that the command model generated by the server is file “foo” “;date”, at this time, I use a backslash to escape the quotation mark ② into a normal character, then the quotation mark ① and ③ will be combined naturally, and then use the comment symbol to ④ Comment out the quotation marks, and ;date is directly visible on the command line. Logically, I can use file “foo\” “;id #bar” to inject arbitrary commands and escape the quotation marks again:
Note, the comment character # needs to be URL encoded as %23.
Okay, remember that I found a regular loophole that couldn’t filter backslashes. The quotes in wargame can no longer restrain me. Although they can’t be used directly at the moment, they at least let me take a step forward.
0x03 option injection
Continue to look at the command execution code on lines 29 and 30. Obviously this has something to do with the command injection vulnerability. There are three common methods of command injection: inject commands with command separators, inject commands with command substitutions, and inject commands with command options.
The command separator is injected into the command. Command separators include line break (\n), semicolon (;), logical AND (&&, &), logical OR (||,|), if you can use %1A in the win batch script. such as:
The command substitution character is injected into the command. The shell gives priority to the execution of the commands in the command substitution symbol, so that the operation and maintenance personnel can use the output of the previous command as the input of the next command. Command substitution characters include $(…), backticks `…`. such as:
Command options are injected into commands. Command option (option) and command argument (argument) are two concepts, and many documents confuse them. such as:
Among them, -d is the command option and /tmp/ is the command parameter. Many times, the blue team filters out all command separators and command substitutions. Although I cannot inject commands directly, I can inject other command options. This gives me a lot of room for imagination. Some options can read files, and some Can write files and even execute other commands. For example, if you have a page, you can package the web directory into the archive file you specify, and enter it as the archive file name $archive parameter. The server filters all commands to inject relevant characters and call system(“tar -cf”. $archive. “* “) Excuting an order:
But I successfully executed the command id by injecting the –checkpoint and –checkpoint-action=exec options of the tar command:
What is executed in wargame is the file command. Check out its useful options. For example, if there is an option to read the file, search for read in man and find the -f option:
Take a closer look, this parameter can not read the content of the display file, just get the file list from the file, which is meaningless (.・_・.). Wait, what’s the hint in the error message:
By injecting the command option -f, I can read the contents of the wargame file.
0x04 completely disintegrated
Combining multiple independent vulnerabilities into a vulnerability chain to complete the targeted attack is definitely my G point. Looking back at the previous results, the backslash could not be filtered due to the wrong writing of the regular expression; through the backslash, you can escape the quotation marks; through the quotation mark escape, creating conditions for command option injection; by injecting the -f option to achieve flag file reading take. The rule for filtering backslashes is the loose screw.
Now, before the target is attacked, there is one problem left. I don’t know the flag file path and file name. The first thing that comes to mind is brute force. earth! It’s really rustic, using common flags, FLAG, f14g and other common flags to name it again, and it’s no gain. Put another way, wildcard pattern matching. Now it’s foreign style.
Globbing patterns are also called pathname expansion. Simply put, when expressing file names/directory names or paths, you can use? To represent any visible character and * to represent zero or more Visible characters, use [az] to represent the range of characters, with the only exception, files or directories starting with. And paths separated by / must be explicitly written, otherwise they cannot be patterned.
For example, I don’t know that there is a file named FindMe in the /tmp/ directory. However, using wildcards for multiple tests, not only the existence of the file is spied out, but the content of the file is successfully viewed:
OK, now everything is ready, attack CMDi_lab/escaping_quotation. With the previous analysis, I constructed the payload f1=foo\&f2=-f? Bar #, converted file “foo” “bar” to file “foo\” “-f? Bar #”, and guessed the file name only One character file:
Obviously, no such file was found. The same idea, with the help of burp, automatically finds all files whose file name length is [1, 16]:
After running, I still didn’t find any files. This is strange. As mentioned earlier, wildcards cannot match ., could it be hidden files, adjust the load, f1=foo\&f2=-f .? bar #, break again:
Find the directory named .f1a9_, continue to adjust the load f1=foo\&f2=-f .f1a9_/.? bar #, break through:
Find the file named .f1a9_/.flag_15_here.txt, and visit with the exact path:
WTF! It shouldn’t, it doesn’t make sense logically. Could it be that there are filtered characters in the new part of the payload? Going back to the previous regular source code, the flag keyword was indeed filtered. I replaced it with a wildcard, and the payload became f1=foo\&f2=-f .f1a9_/.fl?g_15_here.txt bar #. In addition, before the command option -f A space should be added, and the final payload is f1=foo\&f2= -f .f1a9_/.fl?g_15_here.txt bar #, let’s send:
What a pleasant attacking experience!
0x05 Unexpected solution
Regex does not filter backslashes correctly, uses backslashes to escape quotation marks, wildcard mode to guess the path, inject command options to read files, contrived, awkward! I wrote the above to make up the word count and increase the space. The actual attack method is not like this.
Carefully audit the code for regular filtering. Using \\ instead of \\\\ to express backslashes will not only fail to filter the backslashes correctly, but will also cause a chain reaction. You see, \\ is followed by |\n:
As mentioned earlier, the \\ result string is escaped to the regular engine and becomes \, which is combined with |\n to become \|\n, which is misunderstood by the regular engine to match the combination of vertical bars and newlines. When I enter the combination of a vertical bar and a newline character, the confirmation is filtered:
In other words, the server only filters |\n and releases \n. With a newline character, I can directly inject new commands, for example, execute command id:
Now that the command can be injected, check the flag! The command grep -r.. Can view the contents of all files in the current directory. The server filtered grep. I easily bypassed it with internal empty variables (g$1rep -r. .), or bypassed invalid escapes (g\rep -r. .), or wildcard bypass (/bin/gr?p -r.. or /bin/gr[df]p -r. .).
OK, construct the load ?f1=foo\&f2=%0a/bin/gr[d-f]p+-r+.+.+%23, the page displays:
0x06 end of story
In the general business scenario, the server executes the packaging command to compress several fixed directories, allows the user to enter the archive file name, and multiple times spy to confirm the zip command used, similar to:
Among them, the archive file name archive.tar is controllable. The server regularly filters all command separators, command substitutions, and other meta-characters. At the same time, it prohibits egress traffic, which obviously cannot inject commands directly.
I tried it and found that the horizontal line (-) is allowed, which tells me that I can inject command options. I started to analyze the options of the environment zip itself that I can use. First search for the keyword execute and find nothing; then search for the keyword command and find the options –unzip-command (abbreviated -TT) and –test (abbreviated -T), allowing users to specify a third-party program to verify the integrity of the archive file :
In other words, the options -T and –unzip-command can inject a new command id:
Successfully got the bounty.
The command injection attack can be realized by using the environment itself in addition to the conventional command separator and command substitution character.
Note 1: The prototype of wargame comes from the wargame written by Kaibro, see http://final.kaibro.tw:10002/;
Note 2: The source code of escaping_quotation and more command injection related wargame can be found at https://github.com/yangyangwithgnu/CMDi_lab