A ctf question introduces vsprintf vulnerability exploitation techniques

Preface

I participated in DownunderCTF some time ago, and there was a question that I worked on for a long time, but could not solve in the end. After watching wp, I found that this question was very interesting, and it involved some skills of vsprintf, so I wrote it down to share.

text

Let’s look directly at the topic smooth-jazz

smooth-jazz

The question code is as follows:

<?php
function mysql_fquery($mysqli, $query, $params) {
  return mysqli_query($mysqli, vsprintf($query, $params));
}

if (isset($_POST['username']) && isset($_POST['password'])) {
  $mysqli = mysqli_connect('db', 'challuser', 'challpass', 'challenge');
  $username = strtr($_POST['username'], ['"' => '\\"', '\\' => '\\\\']);
  $password = sha1($_POST['password']);

  $res = mysql_fquery($mysqli, 'SELECT * FROM users WHERE username = "%s"', [$username]);
  if (!mysqli_fetch_assoc($res)) {
     $message = "Username not found.";
     goto fail;
  }
  $res = mysql_fquery($mysqli, 'SELECT * FROM users WHERE username = "'.$username.'" AND password = "%s"', [$password]);
  if (!mysqli_fetch_assoc($res)) {
     $message = "Invalid password.";
     goto fail;
  }
  $htmlsafe_username = htmlspecialchars($username, ENT_COMPAT | ENT_SUBSTITUTE);
  $greeting = $username === "admin" 
      ? "Hello $htmlsafe_username, the server time is %s and the flag is %s"
      : "Hello $htmlsafe_username, the server time is %s";

  $message = vsprintf($greeting, [date('Y-m-d H:i:s'), getenv('FLAG')]);

  fail:
}
?>
<!DOCTYPE html>
<html>
<head>
  <title>Smooth Jazz</title>
  <style>
    body {
      background-color: #f8f8f8;
      font-family: Arial, sans-serif;
    }

    .container {
      max-width: 400px;
      margin: 100px auto;
      padding: 20px;
      background-color: #fff;
      border-radius: 5px;
      box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
      text-align: center;
    }

    h1 {
      color: #333;
    }

    form {
      margin-top: 20px;
    }

    label, input {
      display: block;
      margin-bottom: 10px;
    }

    input[type="text"],
    input[type="password"] {
      width: 100%;
      padding: 10px;
      border: 1px solid #ccc;
      border-radius: 4px;
      box-sizing: border-box;
    }

    input[type="submit"] {
      width: 100%;
      padding: 10px;
      background-color: #4287f5;
      color: white;
      border: none;
      border-radius: 4px;
      cursor: pointer;
    }

    .music-player {
      margin-top: 20px;
    }

    h2 {
      color: #333;
    }

    audio {
      width: 100%;
      margin-top: 10px;
    }

    .message {
      margin-top: 10px;
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>Smooth Jazz</h1>
    <form method="post">
      <label for="username">Username:</label>
      <input type="text" id="username" name="username" placeholder="Enter your username">

      <label for="password">Password:</label>
      <input type="password" id="password" name="password" placeholder="Enter your password">

      <input type="submit" value="Login">
    </form>
    <div class="music-player">
      <audio src="/offering-larry-stephens.mp3" id="audio"></audio>
      If you are stuck, you can <a href="javascript:document.getElementById('audio').play()">listen to some smooth jazz</a>.
    </div>
    <div id="message" class="message">
      <p><?= $message ?? '' ?></p>
    </div>
  </div>
</body>
</html>

Let’s talk about the overall logic first.

1. First determine whether the username exists

2. SQL query whether the corresponding user name and password exist in the database

3. Finally, $username must be equal to (===)admin to get the flag.

In fact, at first I thought it was wide byte injection, because I constructed admin%df’ and found that I could bypass the first step, but I couldn’t inject it. After checking the database encoding, there was no gbk encoding. In fact, this involves another test point:

UTF truncation: UTF truncation will truncate invalid content. For example, admin and admin%ff should be the same.

So I can bypass the first step here in this way, but this is not a wide byte injection. And it will be stronger than admin in the future. This === impression cannot be bypassed in non-special circumstances. And according to the code, the injection point should be in the second query statement.

It seems to be unsolvable now, but there is a vsprintf at the very beginning of the code. I also ignored it during the competition. I thought the test point was below. The common usage of vsprintf here is based on the format indicator in the format string:

# %[argnum$][flags][width][.precision]specifier.
# 必须参数就是%specifier 中间都是可选参数
#一些用法
# %表示要被格式的参数 后面跟类型 %s就是字符串
$string = vsprintf('Hello, %s! Today is %s.', ['John', 'Monday']);
# %1$s 表示第一个字符要被格式成字符串,%2$d 表示第二个字符要被格式成数字。
$result = vsprintf('Name: %1$s, Age: %2$d', ['John', 25]);
# %1$'a10 表示单引号后的第一个字符要填充 填充10次。然后拼接后面的3
$result = vsprintf("Like %1\$'a10s", ["3"]);

Of course, it is recommended to read the official documentation to learn more about all usages:

https://www.php.net/manual/zh/function.vsprintf.php

If we insert a %1$c and then generate a string starting with 34 from the sha1 of the password (the char type only uses the previous char format string):
Insert image description here

Then when we format the string, we get a double quote, which means we can close it.

Then our injection payload can be

username=admin%ff%1$c||1#&password=668

\xff is for utf8 truncation to bypass the admin's judgment in the SQL query part. Anything after the truncation will not be compared with the admin.
Insert image description here

But there is still a problem, how to break through ==='admin’ to force the judgment to print the flag?

$htmlsafe_username = htmlspecialchars($username, ENT_COMPAT | ENT_SUBSTITUTE);
  $greeting = $username === "admin" 
      ? "Hello $htmlsafe_username, the server time is %s and the flag is %s"
      : "Hello $htmlsafe_username, the server time is %s";

  $message = vsprintf($greeting, [date('Y-m-d H:i:s'), getenv('FLAG')]);

We still use vsprintf here. Since we can’t bypass === “admin”, can we create a variable placement position to print out getenv(‘FLAG’) when formatting the string?

If we directly insert %2 s, then an error will be reported when s q l injects the formatted string, because there is only one parameter [ p a s s w o r d ], and what we need is success. Execute s q l injection and get something like s in the last v s pr i n t f Then an error will be reported when sql injects the formatted string, because there is only one parameter [password]. What we need is to successfully execute sql injection and get it in the last You can get something like %2 in vsprintf s then inject formatting characters insql An error will be reported when stringing, because there is only one parameter[password], what we need is to successfully execute sql and inject it in the last vsprintf can get fields similar to s to format the flag and write it in, then we need The second variable position is not formatted when splicing sql statements. The flag is formatted at the last vsprintf position:

The key to this step is htmlspecialchars

$htmlsafe_username = htmlspecialchars($username, ENT_COMPAT | ENT_SUBSTITUTE);
//这行代码将 $username 变量的值进行 HTML 转义,并将转义后的结果赋给 $htmlsafe_username 变量。
//第一个参数 $username 是需要进行转义的字符串,它的值被传递给 htmlspecialchars() 函数进行处理。
//第二个参数 ENT_COMPAT | ENT_SUBSTITUTE 是转义模式,用于指定转义的规则。
//ENT_COMPAT 表示只转义双引号,将双引号转换为 "。其他特殊字符不转义。
//ENT_SUBSTITUTE 表示将无法转义的字符用 Unicode 替代符号替代,而不是忽略或删除它们。

Using > encoded as > we can construct %1 ′ > '>%2 >s

We can see the official manual for what $' means:

Insert image description here

That is, the above string needs to be filled> but the quantity is not written, so if 0 is filled, it will be empty. %1 ′ > will become empty when v s p r i n t f is processed , only '> will become empty when processed by vsprintf, leaving only %2 >presentvspr intf处り时时会变生空,下下 s

The sql statement becomes:

SELECT * FROM users WHERE username = "admin"||1#%2$s" AND password = "34c66477519b949b09b45e131347c17b5822a30a"SELECT * FROM users WHERE username = "admin"||1#%2$s" AND password =

Here is the explanation why%1 ′ > '>%2 >s It is not as we understand that both parameters should be formatted. The key is the document.

%[argnum$][flags][width][.precision]specifier.

The official documentation explains how to use this function as follows, where % and specifier are required. That is to say, %s is a minimalist mode. When we use %1$'<, have you discovered what the specifier of this format should be?

No, there is no specifier, so %1 ′ > '>%2 >s treats % as a specifier and this % escapes:
Insert image description here

In fact, output % requires %% escaping and this principle is almost exactly the same.

Subsequent username needs to be passed in htmlspecialchars($username):

admin%1$c||1#%1$'>%2$s

The spliced ​​parameters at this time are:

$greeting = Hello admin%1$c||1#%1$'>%2$s, the server time is %s
$message = vsprintf($greeting, [date('Y-m-d H:i:s'), getenv('FLAG')]);
1、%1$c 被一个字符填充
2、%1$'&g 被 2023 填充,因为&后面没有数字,所以没有使用&填充,后面specifier标志为g表示通用格式。可以看官方手册了解详情
3、%2$s 被真正的flag填充 最后的%s第一个时间字符串填充

Finally we got the flag
Insert image description here

Expand

Since %specifier is necessary in the vsprintf function formatting process
, it is often used to escape single quotes. For example, in SQL statements, there are often ' that need to be closed. The code escapes ' and we cannot close it, so we can construct:

%1$'or(1=1)#

After escaping, it becomes

%1$\'or(1=1)#

When going through vsprintf, the single quotes are escaped.
Insert image description here

Because the specifier is required, the slash is eaten as a type, and the single quotes naturally come out. This technique can also be used to escape other characters, which is very useful in SQL injection.

In fact, the vsprintf function of PHP is almost the same as the vsprintf in C language. In the future, if you encounter this string formatting vulnerability in pwn, you can also refer to the web vulnerability tips.

More CTF question banks【Click to receive

Network security learning resource sharing:

Finally, I would like to share with you a complete set of network security learning materials that I have studied myself. I hope it will be helpful to friends who want to learn network security!

Getting Started with Zero Basics

For students who have never been exposed to network security, we have prepared a detailed learning and growth roadmap for you. It can be said to be the most scientific and systematic learning route. It will be no problem for everyone to follow this general direction.

[Click to receive] CSDN gift package: "Hacker & Network Security Introduction & Advanced Learning Resource Package" free sharing

1. Learning roadmap

Insert image description here

There are a lot of things to learn about attack and defense. I have written down the specific things you need to learn in the road map above. If you can learn them all, you will have no problem taking on private work.

2. Video tutorial

Although there are many learning resources on the Internet, they are basically incomplete. This is a video tutorial on network security that I recorded myself. I have accompanying video explanations for every knowledge point in the roadmap above. [Click to receive the video tutorial]

Insert image description here

I also compiled the technical documents myself, including my experience and technical points in participating in large-scale network security operations, CTF and digging SRC vulnerabilities. There are also more than 200 e-books[Click to receive it Technical Documentation]

Insert image description here

(They are all packaged into one piece and cannot be expanded one by one. There are more than 300 episodes in total)

3. Technical documents and e-books

I also compiled the technical documents myself, including my experience and technical points in participating in large-scale network security operations, CTF and digging SRC vulnerabilities. There are also more than 200 e-books[Click to receive it Books]

Insert image description here

4. Toolkit, interview questions and source code

"If you want to do your job well, you must first sharpen your tools." I have summarized dozens of the most popular hacking tools for everyone. The scope of coverage mainly focuses on information collection, Android hacking tools, automation tools, phishing, etc. Interested students should not miss it.

Insert image description here

Finally, here are the interview questions about network security that I have compiled over the past few years. If you are looking for a job in network security, they will definitely help you a lot.

These questions are often encountered when interviewing Sangfor, Qi Anxin, Tencent or other major companies. If you have good questions or good insights, please share them.

Reference analysis: Sangfor official website, Qi’anxin official website, Freebuf, csdn, etc.

Content features: Clear organization and graphical representation to make it easier to understand.

Summary of content: Including intranet, operating system, protocol, penetration testing, security service, vulnerability, injection, XSS, CSRF, SSRF, file upload, file download, file inclusion, XXE, logical vulnerability, tools, SQLmap, NMAP, BP, MSF…

Insert image description here

Due to limited space, only part of the information is displayed. You need to click the link below to obtain it
CSDN gift package: "Hacker & Network Security Introduction & Advanced Learning Resource Package" Share for free

Guess you like

Origin blog.csdn.net/HUANGXIN9898/article/details/133028977