#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <sys/socket.h>

#define BUF_SIZE 1024

 

void error_handling(char *message);

 

int main(int argc, char *argv[]){

int sock;

char message[BUF_SIZE];

int str_len, recv_len, recv_cnt;

struct sockaddr_in serv_addr;

 

if(argc!=3){

printf("Usage : %s <IP> <PORT>\n",argv[0]);

exit(1);

}

 

sock = socket(PF_INET, SOCK_STREAM, 0);

if(sock==-1){

error_handling("socket() error");

}

 

memset(&serv_addr, 0, sizeof(serv_addr));

serv_addr.sin_family = AF_INET;

serv_addr.sin_addr.s_addr = inet_addr(argv[1]);

serv_addr.sin_port = htons(atoi(argv[2]));

 

if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1){

error_handling("connect() error");

}

else{

printf("Connect……");

}

 

while(1)

{

fputs("Input message(Q to quit)", stdout);

fgets(message, BUF_SIZE, stdin);

if(!strcmp(message, 'q\n') || !strcmp(message, 'Q\n')){

break;

}

str_len = write(sock, message, strlen(message));

 

recv_len = 0;

while(recv_len < str_len)

{

recv_cnt = read(sock, &message[recv_len], BUF_SIZE-1);

if(recv_cnt == -1){

error_handling("read() error");

}

recv_len += recv_cnt;

}

message[recv_cnt]=0;

printf("Message from server : %s", message);

}

close(sock);

return 0;

}

 

void error_handling(char *message){

fputs(message, stderr);

fputc('\n', stderr);

exit(1);

}

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <sys/socket.h>

 

#define BUF_SIZE 1024

void error_handling(char *message);

 

int main(int argc, char *argv[]){

 

int serv_sock, clnt_sock;

char message[BUF_SIZE];

int str_len, i;

 

struct sockaddr_in serv_addr, clnt_addr;

socklen_t clnt_adr_sz;

 

if(argc!=2){

printf("Usage : %s <port>\n", argv[0]);

exit(1);

}

 

serv_sock = socket(PF_INET, SOCK_STREAM, 0);

if(serv_sock==-1){

error_handling("socket() error");

}

 

memset(&serv_addr, 0, sizeof(serv_addr));

serv_addr.sin_family = AF_INET;

serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

serv_addr.sin_port = htons(atoi(argv[1]));

 

if(bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1){

error_handling("bind() error");

}

 

if(listen(serv_sock, 5)== -1){

error_handling("listen() error");

}

 

clnt_adr_sz = sizeof(clnt_addr);

 

/* 五つのクライアントにサービスを提供するための繰り返し文. */

for (i = 0; i < 5; i++)

{

clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_adr_sz);

if(clnt_sock == -1){

error_handling("accept() error");

}

else{

printf("Connected client %d \n", i+1);

}

/* 実際のエコサービスを提供する部分 */

while((str_len = read(clnt_sock, message, BUF_SIZE)) != 0){

write(clnt_sock, message, str_len);

}

close(clnt_sock);

}

 

close(serv_sock);

return 0;

}

 

void error_handling(char *message){

fputs(message, stderr);

fputc('\n', stderr);

exit(1);

}

 

◆ソケットの生成

#include <sys/socket.h>

 

int socket (int domain, int type, int protocol);

  •  成功時 ファイルディスクリプタ, 失敗時 -1 返還

domain :ソケットが使用するプロトコル情報

type :ソケットのデータ転送方式の情報

protocol :パソコン間通信に使用するプロトコル情報

 

domain

名前

プロトコルシステム

PF_INET

IPv4システム

PF_INET6

IPv6システム

PF_LOCAL

ローカル通信のためのUNIXプロトコルシステム

PF_PACKET

Low Levelソケットのためのプロトコルシステム

PF_IPX

IPXノベル?プロトコルシステム

type

SOCK_STREAM

接続指向ソケット

SOCK_DGRAM

非接続指向ソケット

TCPソケット=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
UDPソケット=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
 
◆住所情報の表現

struct sockaddr_in

{

sa_family_t        sin_family;  //住所システム(Address Family)

uint16_t             sin_port;     //16ビット TCP/UDP PORT番号

struct in_addr    sin_addr;   //32ビット IP住所

char                   sin_zero[8] //使われてない   

};

そして、上記構造体定義に使われてる、構造体in_addrは下記のように定義されている。
これは32ビット住所情報を入れられるように定義されている。

struct in_addr

{

in_addr_t    s_addr;   //32ビット IPv4インターネット住所

};

 
メンバー sin_family

Address Family

意味

AF_INET

AF_INET6

AF_LOCAL

IPv4インターネットプロトコルに適用する住所システム

IPv6インターネットプロトコルに適用する住所システム

ローカル通信のためのUNIXプロトコルの住所システム

 
メンバーsin_port

16ビットPORT番号をセーブする(ネットワークバイト順)

 

メンバーsin_addr

32ビットIP住所情報をセーブする(同じくネットワークバイト順)

 

バイト順番の変換(Endian Conversions)

sockaddr_in構造体変数に値を入れる前にネットワークバイト順番に変換するので、バイト順番の変換を手伝ってくれる関数の一覧

-unsigned short htons(unsogned short);

-unsigned short ntohs(unsigned short);

-unsigned long htonl(unsigned long);

-unsigned long ntohl(unsigned long);

hはホスト、nはネットワーク、sはshort、lはlong

 

INADDR_ANY

サーバソケットの生成中サーバのIP住所を入力するのは面倒くさいから次のように初期化すれば良い

struct sockaddr_in addr;

char *serv_port = "9190";

memset(&addr, 0, sizeof(addr));

addr.sin_family = AF_INET;

addr.sin_addr.s_addr = htonl(INADDR_ANY);

addr.sin_port = htons(atoi(serv_port));

今回もリモート問題なので接続し、ソースコードを見てみると

長い。。。。

Stage5まであってそれぞれの値と比較してあってれば次のStageに行く感じ

そして、実際に値をいちいち入れるのは無理だから、ソースコードを書いて解いて行く

まず、ステージ1から

if(argc != 100) return 0;

    if(strcmp(argv['A'],"\x00")) return 0;

    if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0;

    printf("Stage 1 clear!\n"); 

だから

argcが100で、argv['A']には\x00

argv['B']には\x20\x0a\x0dが入っていればクリアらしい

まず、argv_hを100個作って、

argv['A']には\x00

argv['B']には\x20\x0a\x0d

そしてargv[100]にはNULLを入れる(C言語の配列の一番最後にはNULL)

セッティングが終わったらexecveシステムコールで実行ファイルのパスとセッティングしたargv_hを渡す。

そして、実行して見ると

ステージ1はクリア!!

ステージ2は

char buf[4];

    read(0, buf, 4);

    if(memcmp(buf, "\x00\x0a\x00\xff", 4)) return 0;

    read(2, buf, 4);

        if(memcmp(buf, "\x00\x0a\x02\xff", 4)) return 0;

    printf("Stage 2 clear!\n");

ステージ2がどうすればいいか分からなくて色々検索したらpipeを使えばいいと書いてあったので

pipeについて勉強もできた!!

簡単に言うとpipe関数を使うとプロセス間通信ができるということだ

順番に説明すると

1.両方向通信のために二つのpipeを作る

2.childプロセスを生成する

3.子プロセスはwriteは要らないからwriteを閉じて、read側を0(stdin)と2(stderr)に割り当てる

4.execveシステムコールでchildプロセスをinputプロセスに変える

4.親プロセスはreadは要らないからreadを閉じて、write側にクリア条件の値を書いてあげる

で、実行したら

ステージ2までクリア!!

次は、ステージ3

if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;

    printf("Stage 3 clear!\n");

これは、execveシステムコールに環境変数を設定して渡せばOK!

childプロセスのexecveに追記して、実行すると

ステージ3もクリア!!

次はステージ4

FILE* fp = fopen("\x0a", "r");

    if(!fp) return 0;

    if( fread(buf, 4, 1, fp)!=1 ) return 0;

    if( memcmp(buf, "\x00\x00\x00\x00", 4) ) return 0;

    fclose(fp);

    printf("Stage 4 clear!\n");   

これは"\x0a"というファイルに"\x00\x00\x00\x00"を書いて置けばOKっぽい!

下から4番目からが追記したもの!

ファイルをオープンしてwriteで"\x00\x00\x00\x00"を書いてあげて、実行すると

ステージ4もクリア!!

次はステージ5

int sd, cd;

    struct sockaddr_in saddr, caddr;

    sd = socket(AF_INET, SOCK_STREAM, 0);

    if(sd == -1){

        printf("socket error, tell admin\n");

        return 0;

    }

    saddr.sin_family = AF_INET;

    saddr.sin_addr.s_addr = INADDR_ANY;

    saddr.sin_port = htons( atoi(argv['C']) );

    if(bind(sd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){

        printf("bind error, use another port\n");

            return 1;

    }

    listen(sd, 1);

    int c = sizeof(struct sockaddr_in);

    cd = accept(sd, (struct sockaddr *)&caddr, (socklen_t*)&c);

    if(cd < 0){

        printf("accept error, tell admin\n");

        return 0;

    }

    if( recv(cd, buf, 4, 0) != 4 ) return 0;

    if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0;

    printf("Stage 5 clear!\n");

ソースコードをみると

saddr.sin_port = htons( atoi(argv['C']) ); ここでargv['C']の値を設定するから

任意の番号をargv['C']に設定して、ソケットに"\xde\xad\xbe\xef"を書いてあげれば良い

int sock; からが追記したソースコードだ

これで、実行するとステージ5までクリアはできるけど、flagが出力されない。。。

調べたら、現在の経路(実行ファイルがあるところ/home/input2には権限がないため、/tmp/私が作ったフォルダーの中)

にはflagファイルがないためだという。。

解決方法はシンボルリンクを作ればOKだという記事をみたので

やってみたらflagが出力された!!

でも、スクリーンショット取るの忘れた。。

今回もリモート問題だからリモートでアクセスし、ソースコードを見る

まず、keyに値を入力(10進数)してrandom変数と、XOR演算結果が0xdeadbeefになればOKらしい

だが。。。rand()←こうするとランダムで生成されるけど、毎度同じ数字で生成される

本当にランダムで値を作りたいのであれば、srand()を使うべき

 

ということはrand()で生成された値を見つけて攻略すれば良さそう

なので、gdbで見てみる

まず、rand()関数が呼び出された後のところ(main+18)にブレイクポイントを設定して実行

そして、レジストリを見ると

raxに0x6b8b4567が入っている。これがrand()関数で生成された値だ。(関数はreturnする時、raxに戻り値を入れる)

では、keyの値と0x6b8b4567をXOR演算して、0xdeadbeefがなるようにすれば良い

これが0xdeadbeefの2進数

これが0x6b8b4567の2進数

 

key        = ?

                   XOR
random     = 0110 1011 1000 1011 0100 0101 0110 0111
                   結果
deadbeef   = 1101 1110 1010 1101 1011 1110 1110 1111

 

XOR演算は同じ値だと0になり、違う値だと1になるから

 

key        = 1011 0101 0010 0110 1111 1011 1000 1000
random     = 0110 1011 1000 1011 0100 0101 0110 0111

deadbeef   = 1101 1110 1010 1101 1011 1110 1110 1111

こうなれば良い!

1011 0101 0010 0110 1111 1011 1000 1000を10進数に変換すると

3,039,230,856になるから、やってみよう

 

出た!!成功!!