Preface
In CTF, the topic of md5 is too common. Although there are many articles in this area, they are relatively scattered. Here we mainly focus on the problems of weak type and strong collision of md5 encountered during my study and competition from shallow to deep. Sort it out.
This article involves practical exercises on knowledge points: a brief discussion on md5 weak type comparison and strong collision related experiments: Weekly CTF (This course aims to provide some CTF topics to friends who are interested in CTF, so that everyone can learn relevant knowledge through these topics. )
basic knowledge
There are two comparison symbols in php == and ===== When comparing, if you compare a number and a string or compare a string involving digital content, the string will be converted to a number and the comparison will follow Numerically.
=== When comparing, it will first judge whether the types of the two strings are equal, and then compare.
Strings that start with 0e and are numbers, and weak type comparisons are all equal to 0.
== compare
Test code
<?php
if (isset($_POST['a']) and isset($_POST['b']))
{
if ($_POST['a'] != $_POST['b'])
{
if (md5($_POST['a']) == md5($_POST['b']))
echo 'flag';
else
echo 'you are wrong';
}
else echo "请输入不同的a,b值";
}
Solution 1
Since md5 cannot encrypt an array, it will return NULL when encrypting an array
So, we can pass in two arrays
Solution 2
You can pass in two md5 encrypted strings starting with 0e. It should be noted that this string starting with 0e can only be a pure number, so that php will convert it to 0 when performing scientific calculations. . You can search for strings that start with 0e and are encrypted with md5, or you can write your own code and provide the following script.
<?php
for($a=1;$a<=1000000000;$a++){
$md5 = md5($a);
if(preg_match('/^0e\d+$/',$md5)){
echo $a;
echo "\n";
echo $md5;
echo "\n";
}
}
s1502113478a
0e861580163291561247404381396064
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s155964671a
0e342768416822451524974117254469
s1184209335a
0e072485820392773389523109082030
===Compare
<?php
if (isset($_POST['a']) and isset($_POST['b']))
{
if ($_POST['a'] != $_POST['b'])
{
if (md5($_POST['a']) === md5($_POST['b']))
echo 'flag';
else
echo 'you are wrong';
}
else echo "请输入不同的a,b值";
}
?>
Solution 1:
You can also pass in two arrays, but it is no longer suitable to pass in two strings starting with 0e, because === is a strong collision of md5 and is strictly filtered.
Solution 2:
Use md5 to encrypt two identical strings to bypass filtering.
How to generate two different strings, but MD5 is the same? After referring to how to build the same MD5 with different values , we can use the fast MD5 collision generator to build two strings with the same MD5 but completely different content.
structure
Create a text file, write any file content, and name it ywj.txt (source file)
Run fastcoll to output the following parameters. -p is the source file, -o is the output file
fastcoll_v1.0.0.5.exe -p ywj.txt -o 1.txt 2.txt
test
Test the 1.txt and 2.txt files produced
<?php
function readmyfile($path){
$fh = fopen($path, "rb");
$data = fread($fh, filesize($path));
fclose($fh);
return $data;
}
echo '二进制md5加密 '. md5( (readmyfile("1.txt")));
echo "</br>";
echo 'url编码 '. urlencode(readmyfile("1.txt"));
echo "</br>";
echo '二进制md5加密 '.md5( (readmyfile("2.txt")));
echo "</br>";
echo 'url编码 '. urlencode(readmyfile("2.txt"));
echo "</br>";
二进制md5加密 8e4ef6c69a337c0de0208455ee69a416
url编码 1%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%A3njn%FD%1A%CB%3A%29Wr%02En%CE%89%9A%E3%8EF%F1%BE%E9%EE3%0E%82%2A%95%23%0D%FA%CE%1C%F2%C4P%C2%B7s%0F%C8t%F28%FAU%AD%2C%EB%1D%D8%D2%00%8C%3B%FCN%C9b4%DB%AC%17%A8%BF%3Fh%84i%F4%1E%B5Q%7B%FC%B9RuJ%60%B4%0D7%F9%F9%00%1E%C1%1B%16%C9M%2A%7D%B2%BBoW%02%7D%8F%7F%C0qT%D0%CF%3A%9DFH%F1%25%AC%DF%FA%C4G%27uW%CFNB%E7%EF%B0
二进制md5加密 8e4ef6c69a337c0de0208455ee69a416
url编码 1%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%A3njn%FD%1A%CB%3A%29Wr%02En%CE%89%9A%E3%8E%C6%F1%BE%E9%EE3%0E%82%2A%95%23%0D%FA%CE%1C%F2%C4P%C2%B7s%0F%C8t%F28zV%AD%2C%EB%1D%D8%D2%00%8C%3B%FCN%C9%E24%DB%AC%17%A8%BF%3Fh%84i%F4%1E%B5Q%7B%FC%B9RuJ%60%B4%0D%B7%F9%F9%00%1E%C1%1B%16%C9M%2A%7D%B2%BBoW%02%7D%8F%7F%C0qT%D0%CF%3A%1DFH%F1%25%AC%DF%FA%C4G%27uW%CF%CEB%E7%EF%B0
As you can see, the results of the binary md5 encryption of the 1.txt and 2.txt files are exactly the same. Since the files 1.txt and 2.txt contain invisible characters, they need to be url encoded before use. It can be seen that the two strings after url encoding are not exactly the same, which meets our need to enter two different parameters.
When the question limit cannot be passed in an array, but only a string, as in the following example, only solution 2.
<?php
if((string)$_GET['a'] !== (string)$_GET['b'] && md5($_GET['a'])===md5($_GET['b'])){
echo "you are right";
}
else {
echo "you are wrong";
}
HECTF ezphp
Source code
<?php
error_reporting(0);
highlight_file(__file__);
include('flag.php');
$string_1 = $_GET['str1'];
$string_2 = $_GET['str2'];
if($_GET['param1']!==$_GET['param2']&&md5($_GET['param1'])===md5($_GET['param2'])){
if(is_numeric($string_1)){
$md5_1 = md5($string_1);
$md5_2 = md5($string_2);
if($md5_1 != $md5_2){
$a = strtr($md5_1, 'cxhp', '0123');
$b = strtr($md5_2, 'cxhp', '0123');
if($a == $b){
echo $flag;
}
}
else {
die("md5 is wrong");
}
}
else {
die('str1 not number');
}
}
?>
First look at the usage of some strtr() function:
The strtr() function converts specific characters in a string.
Observing the source code, four parameters are required. First, param1===param2. Because there are no other restrictions, we can pass in two arrays. For str1 and str2, first str1 can only be numbers, and finally $a == $b, but md5_1 != md5_2, so we cannot pass in two md5 encrypted strings beginning with 0e.
And because the cxhp in str1 and str2 encrypted by md5 will be replaced with 0123, that is, c will be replaced with 0, so a string starting with ce will be replaced with a string starting with 0e.
It is conceivable that as long as you find two strings starting with ce after being encrypted by md5, or one string starting with ce after being encrypted by md5, and one string starting with 0e after being encrypted by md5, you can bypass the filtering.
Construction script
This is the script at the beginning, with few return values and slow execution speed.
<?php
for($a=1;$a<=1000000000;$a++){
$md5 = md5($a);
if(preg_match('/^ce\d+$/',$md5)){
echo $a;
echo "\n";
echo $md5;
echo "\n";
}
}
This is a further optimized script
<?php
for($a = 1; $a <= 100000000; $a++) {
$md5 = strtr(md5($a),'cxhp', '0123');
if(preg_match('/^0e\d+$/', $md5)) {
echo $a;
echo "\n";
echo $md5;
echo "\n";
}
}
?>
Actual combat drill
<?php
function random() {
$a = rand(133,600)*78;
$b = rand(18,195);
return $a+$b;
}
$r = random();
if((string)$_GET['a']==(string)md5($_GET['b'])){
if($a.$r == $b) {
print "Yes,you are right";
}
else {
print "you are wrong";
}
}
?>
Observing the code, there is a rondom method that returns a random number. In this question, we don’t need to know what is returned. We only need to know that the returned number is a string of numbers. Two parameters a and b are passed in, and the string is required to be passed in, and b will be encrypted by md5. Finally, let $a.$r == $b. Because it is a weak type comparison, and only strings can be passed in, what I want is to compare two strings starting with 0e. As we already know, a string starting with 0e can only be a pure number, so php is doing science It will be converted to 0 in the calculation method. So make sure that $a starts with 0e, because $r is a string of numbers, so $a.$r can still be parsed as 0 in PHP. Because $b is the parameter b encrypted by md5, we can pass in the string starting with 0e after md5 encryption.
1