칼리 리눅스 : 칼리 리눅스에 이미 설치되어 있는 툴(airmon-ng, airodump-ng, aircrack-ng 등)을 사용
#1. WEP 암호화 방식이란
무선랜 구간에서 사용되는 암호화 알고리즘으로, 무선랜 표준을 정의하는 IEEE 802.11(1997년)의 규약 중 하나이다. WEP은 대칭키 암호 방식 중 하나인 스트림 암호 방식을 사용합니다. 스트임 암호 방식에서 RC4 암호화 알고리즘을 사용하여 데이터를 암호화 시킵니다. WEP 비밀키(40비트)와 IV(24비트)를 RC4 알고리즘으로 키 스트림을 생성하고, 전달할 데이터와 XOR 해줘서 데이터를 암호화 시켜줍니다.
IV(24비트) : 초기에는 비밀키로만 RC4 알고리즘을 사용하여 암호화 시켰지만 사전공격에 취약해서 IV라는 값을 추가(매번 바뀜) 비밀키(40비트) : 사용자가 설정한 패스워드 또는 공유기에서 생성한 키(4개중 하나) 아래 사이트에서 공유기에서 WEP 키 생성 방법 설명 https://withnotebook.tistory.com/2
#2. 데이터 암호화 과정 원리
1. 40비트 WEP키 와 24비트 IV값을 조합하여 64비트 키를 생성. 2. 64비트 키를 RC4 알고리즘을 통해서 암호화 시켜 RC4 키스트림을 생성. 3. 데이터를 CRC-32 알고리즘을 통해서 ICV 값 생성(무결성 체크를 위한) 4. (데이터+ICV)와 RC4 키스트림을 XOR 함. 5. IV 헤더 : 24비트 IV 키값과 키값(비밀키 4개가 만들어질 경우 어떤 것을 사용할지 번호를 정함) 6. 데이터 와 ICV(암호화) : 4번에서 결과. 7. FCS : ICV 값(데이터 무결성 체크를 위한 값)
#3. 해킹 원리
IV값이 재사용 되는 것과 RC4 알고리즘의 취약점으로 인해 키스트림 중복된 값으로 결과가 나온다. 이러한 이유로, 50000개 이상의 패킷들을 수집후(패킷 수집시 IV값이 노출됩니다.) 재사용된 IV를 분석 후 RC4 알고리즘의 취약점을 이용해서 키스트림을 구하여 암호문을 복호화 시킴. 5000개의 패킷을 생성시 50%이상의 확률로 동일한 IV가 생성(생일 문제)
#4. 칼리리눅스에서 실습
Airmon-ng:무선 네트워크 카드를 모니터 모드로 변경하고 관리하는 데 사용되는 도구 Airodump-ng: 무선 네트워크에서 패킷을 캡처하고 정보를 표시하는 도구 Aircrack-ng: WEP 및 WPA/WPA2 PSK(사전 공격) 비밀번호를 해독하기 위한 도구
1. 모든 패킷을 수집하기 위해서 iwconfig 명령어로 무선랜(wlan0)을 검색해주고, Mode가 Monitor모드인지 확인. Managed 모드 : 목적지가 자신의 랜카드로 들어오는 패킷이 아닌 것은 모두 버림 Monitor 모드 : 목적지가 자신의 랜카드로 들어오지 않는 패킷도 모두 수신.(스니핑시 사용)
4. airodump-ng --bsssid [AP 맥주소] --channel [채널] --write [패킷을 캡쳐한 파일 이름 지정] wlen0(무선랜 이름) 지정한 AP의 패킷을 캡쳐해서 test_wep 이름의 파일로 저장 합니다. #Data(네트워크 트래픽)가 20000~50000 정도 되어야 패킷을 분석해서 IV의 중복값을 찾아낼 수 있음. (Data가 적으면 암호 해독 불가)
5. aircrack-ng test_wep-01.cap(패킷 캡쳐 파일). cap파일을 가지고 암호를 해독.
go get "google.golang.org/api/youtube/v3"
go get "golang.org/x/oauth2"
youtube api와 OAuth 2.0 클라이언트 인증을 사용하기 위해서 외부 패키지를 다운로드해주세요.
package main
import (
"context"
"fmt"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/option"
"google.golang.org/api/youtube/v3"
"io/ioutil"
"log"
"net/http"
)
func main() {
//Read json file (json 파일 읽어옴)
jsonKey, err := ioutil.ReadFile("client.json")
if err != nil {
log.Fatalf("Failed to read JSON file: %v", err)
}
// Set up the OAuth 2.0 configuration (OAuth 2.0 구성 환경 설정)
config, err := google.ConfigFromJSON(jsonKey, youtube.YoutubeReadonlyScope)
if err != nil {
log.Fatalf("Unable to parse client secret file: %v", err)
}
// Create a new OAuth 2.0 client (새로운 OAuth 2.0 클라이언트를 생성 합니다.)
client := getClient(config)
// Set up the YouTube API client using the authenticated client (인증된 클라이언트를 사용하여 유튜브 API 클라이언트를 설정)
service, err := youtube.NewService(context.Background(), option.WithHTTPClient(client))
if err != nil {
log.Fatalf("Unable to create YouTube service: %v", err)
}
// Retrieve the playlist ID for the private playlist you want to access (접근을 원하는 비공개 재생목록 ID)
playlistID := "자신의 계정에 비공개 재생목록의 ID를 넣어주세요"
// Retrieve the playlist items from the private playlist (비공개 재생목록으로부터 재생목록 아이템들을 불러옵니다.)
playlistItemsCall := service.PlaylistItems.List([]string{"snippet"}).
PlaylistId(playlistID). // 재생목록 ID 설정
MaxResults(50) // Adjust the maximum number of results as per your requirements(가져올 재생목록 item 최대값 설정)
playlistItemsResponse, err := playlistItemsCall.Do() // "youtube.playlistItems.list" 호출 실행.
if err != nil {
log.Fatalf("Unable to retrieve playlist items: %v", err)
}
// Process and display the playlist items //가져온 재생목록 아이템들을 제목과 ID 출력
for _, item := range playlistItemsResponse.Items {
title := item.Snippet.Title
videoID := item.Snippet.ResourceId.VideoId
fmt.Printf("Title: %s, Video ID: %s\n", title, videoID)
}
}
// getClient retrieves a valid OAuth 2.0 client.(유효한 OAuth 2.0 클라이언트를 불러옵니다.)
// 토큰트로 *http.Client를 생성하고 반환
func getClient(config *oauth2.Config) *http.Client {
// Retrieve a token, if it exists, or prompts the user to authenticate.
token := getTokenFromWeb(config)
return config.Client(context.Background(), token)
}
// getTokenFromWeb uses the provided OAuth 2.0 Config to request a Token. (제공된 Auth 2.0 구성을 사용해서 토큰을 요청)
// It returns the retrieved Token.(유효한 토큰을 반환)
func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
fmt.Printf("Go to the following link in your browser, then type the "+
"authorization code: \n%v\n", authURL)
//토큰 입력
var code string
if _, err := fmt.Scan(&code); err != nil {
log.Fatalf("Unable to read authorization code: %v", err)
}
token, err := config.Exchange(context.Background(), code)
if err != nil {
log.Fatalf("Unable to retrieve token from web: %v", err)
}
return token
}
- json 파일과 권한 범위를 가지고 *oauth2.Config 구조체를 구성하고 반환함(토큰을 받기 위한 정보가 있음) jsonKey : 3편에서 다운로드한 json파일 youtube.YoutubeReadonlyScope : 접근 가능한 권한 범위(읽기 전용으로 설정함)
- 인증코드를 얻기 위한 동의 페이지 url을 반환해 준다. (사용자 인증 후 인증코드 반환) "state-token" : csrf 방지를 위한 매개변수 oauth2.AccessTypeOffline : 토큰에 관한 설정에 관한 매개변수 (처음에만 인증하고 재인증 필요 없이 리프레쉬 토큰 재발급 가능)
youtube.NewService(ctx, option.WithAPIKey(*apiKey)) 함수를 사용해서 생성 합니다.
첫 번째 인자는 Context이고, 두 번째 인자는 구글 API 클라이언트에 대한 clientOption 입니다. WithAPIKey를 사용하여 apiKey에 대한 clientOption을 반환해 줍니다.
reqPlaylist := service.PlaylistItems.List([]string{"snippet"}). // Set up snippet option
PlaylistId(*playlistID). //Set up playlistID
MaxResults(50) //Set up Max Result
Now we have the missing piece we needed to explain the design of the append built-in function. The signature of append is different from our custom Append function above. Schematically, it's like this:
이제 우리는 append 내장 함수의 디자인을 설명하기 위해서 우리가 필요했던 잃어버린 조각(위에서 부족한 설명에 대한 답)을 가지고 있다. append의 특징은 위에서 본 우리의 커스텀 Append 함수(https://go.dev/doc/effective_go#slices)와 다르다. 개략적으로 이것은 다음과 같다:
func append(slice []T, elements ...T) []T
where T is a placeholder for any given type. You can't actually write a function in Go where the type T is determined by the caller. That's why append is built in: it needs support from the compiler.
여기에서 T는 어떤 주어진 타입에 대한 placeholder(제네릭, 일시적으로 값을 대체하거나 나중에 채워질 것으로 예상되는 위치 )이다. Go에서는 타입 T가 호출자에 의해 결정되는 함수를 작성할 수 없다. 이것이 append가 내장함수인 이유이고: 컴파일러의 지원이 필요하다.
위의 문장에서 보충설명 : 제네릭 T를 사용할 때 타입을 명시해줘야 된다. 안그러면 오류가 난다. 하지만 위에 append 함수는 호출하는 쪽에서 타입을 정할수 있다. 컴파일러의 지원덕분에 가능. 내장함수 append는a := append([]int{}, 1, 2, 3)처럼 호출하는 쪽에서 []int로 정할수 있다. 하지만 커스텀 함수에서 제네릭은 이렇게 타입을 명시해줘야함. 여기서는 any로 해줌.
func Print[T any](a T) {
fmt.Println(a)
}
What append does is append the elements to the end of the slice and return the result. The result needs to be returned because, as with our hand-written Append, the underlying array may change. This simple example
append가 하는 것은 요소들을 slice끝에 삽입하고, 결과를 리턴하는 것이다. 우리의 직접 작성된 Append(https://go.dev/doc/effective_go#slices) 와 같이 실제 배열(슬라이스 안에 배열을 가리키는 포인터)은 바뀔수도 있기 때문에, 결과는 반환되어야할 필요가 있다. 이 간단한 예제는
x := []int{1,2,3}
x = append(x, 4, 5, 6)
fmt.Println(x)
prints [1 2 3 4 5 6]. So append works a little like Printf, collecting an arbitrary number of arguments.
[1 2 3 4 5 6]을 출력한다. 그래서 append는 약간 Printf처럼 동작하고, 임의의 인자들의 수를 수집한다.
But what if we wanted to do what our Append does and append a slice to a slice? Easy: use ... at the call site, just as we did in the call to Output above. This snippet produces identical output to the one above.
그러나 우리의 Append가 하는 것을 하길 원하고, 슬라이스에 슬라이스를 추가하고 싶다면 어떻게 할까? 쉽다: call site(함수 인자에)에 ...을 사용해라, 위에서(https://go.dev/doc/effective_go#Printing) Output(std.Output 함수) 호출에서 했던 것처럼. 아래 snippet(작은 예제)은 위 예제와 동일한 출력을 만든다.
위의 문장에서 보충설명: 위의 custom 함수 Append와 같은 기능을 하고 슬라이스에 슬라이스를 추가하고 싶을 때를 설명하고 있다. 위에 func Println(v ...interface{}) { std.Output(2, fmt.Sprintln(v...)) // Output takes parameters (int, string) } 함수에서 했던 것처럼 ...을 사용 하라는 의미인것으로 보인다.
x := []int{1,2,3}
y := []int{4,5,6}
x = append(x, y...)
fmt.Println(x)
Without that ..., it wouldn't compile because the types would be wrong; y is not of type int.
...없이, 위의 예제는 타입들이 틀리기 때문에 컴파일되지 않을 것이다; 예시 : y는 int 타입이 아니다.
The expression ~string means the set of all types whose underlying type is string. ~string 표현식은 실제 타입이 string인 모든 타입의 집합을 의미한다.
This includes the type string itself as well as all types declared with definitions such as type MyString string. 이것은 type MyString string와 같은 정의와 함께 선언된 모든 타입들 뿐만 아니라 string타입 자신을 포함합니다.
해석 : type키워드를 사용해서 string을 MyString(Named Type)으로 선언하였다. string과 MyString은 서로 다르다.
하지만 ~string을 사용하면 string자신뿐만 아니라 type MyString string로 선언된 타입까지 사용할 수 있다는 뜻이다.
제네릭으로 예제를 만들어보면,
Integer 인터페이스에 int앞에 tilde(~)를 안 넣어주니 오류가 나온다. MyInt는 Integer를 구현하지 않았다고 나오고 가로 안에 아마 Integer안에 int 앞에 ~를 잃어 버렸다고 한다. Integer 인터페이스 안에 int 앞에 tilde(~) 를 넣어주면 올바르게 작동할 것이다. 이렇게 Integer안에 정의한 자료형들을 underlying type도 허용하고 싶으면 앞에 tilde(~)를 넣어주면 된다.