'Log in to a site (Stack Overflow) by Bash

How can I log in to a website using Bash in Linux?

For example, to log in to Stack Overflow I tried many different methods, shown below, but nothing's worked.

wget --save-cookies cookies.txt --keep-session-cookies --post-data="username=blahblah&password=blahblahblha" "https://stackoverflow.com/users/login?ssrc=head&returnurl=https%3a%2f%2fstackoverflow.com%2f"

or

curl --user myusername:mypassword https://stackoverflow.com/users/login?ssrc=head&returnurl=https%3a%2f%2fstackoverflow.com%2f -v

I tried to inspect the element using Chrome to copy the curl request, but it didn't work (maybe it depends on a cookie and is only valid for a certain period of time).

Note that I need to login with username and password not with a cookie.



Solution 1:[1]

For fun only: Connection: keep-alive

Introduction

My goal was to build a Bash environment where the current console (or script) could deal interactively with an authenticated HTTPS session. This could be used to address lot of IoT targets, address any provider platform to monitor personal account, etc.

Unfortunately, this could be badly used for stressing or hacking web platforms (targeting anyone) or even to targeting Stack Overflow's Fanatic badge (dirty cheater!).

My apologies for any bad use of this. Anyway, I'm not responsible of what people do.

Near pure Bash: require only OpenSSL:

... and gpg, but you're free to store your credential in another way.

Preparing some values:

#!/bin/bash
shopt -s extglob

URL='https://stackoverflow.com/'
IFS=: read  -r user pass < <(gpg -qd <socred.gpg)

IFS=/ read -r _ _ hst _ <<<"$URL"

Running the OpenSSL executable as a background task:

exec {wwwE}<> <(: - O)
exec {wwwI}<> <(: - b)
exec {wwwO}< <(
  exec stdbuf -o0 openssl s_client -quiet -connect "$hst":443 <&$wwwI 2>&$wwwE)
osslpid=$!

Then now, there is a little doReq function to create two variables: $cookie and $htstatus, and three arrays: $hthead, $htbody and $hterr:

doReq() {
    hthead=() htbody=() hterr=()
    local target=$1 method=${2:-GET} head=true line cookies
    printf >&$wwwI '%s\r\n' "$method $target HTTP/1.1" "Host: $hst" \
           "User-Agent: aobs/0.01" "Connection: keep-alive" "Accept: */*"
    [ "$cookie" ] && printf >&$wwwI '%s' "$cookie"
    if [ "$method" = "POST" ];then
        printf >&$wwwI '%s\r\n%s\r\n\r\n%s' "Content-Length: ${#3}" \
                       'Content-Type: application/x-www-form-urlencoded' "$3"
    else printf >&$wwwI '\r\n'
    fi
    read -t 10 -ru $wwwO line
    htstatus=${line%$'\r'} ; hthead=("$htstatus")
    while read -t .3 -ru $wwwO line;do
        [ "${line%$'\r'}" ] || head=false;
        if $head ;then
            hthead+=("${line%$'\r'}");
            case $line in
                [sS]et-[cC]ookie:* ) line=${line#*: };
                                 cookies+=("${line%%;*}");;
            esac
        else htbody+=("${line%$'\r'}") ;fi
    done
    if read -t 0 -ru $wwwE;then
        while read -t .1 -ru $wwwE line;do
            hterr+=("${line%$'\r'}")
            case $line in
                depth* | verify* ) ;;
                * ) echo "ERR: $line" ;;
    esac ; done ; fi
    [ ! -v "cookie" ] && [ "${cookies[0]}" ] &&
        printf -v cookie 'Cookie: %s\r\n' "${cookies[@]}"
}

Usage: doReq /file_part_of_URL [method] [post datas]

Let's login:

doReq /users/login POST "email=$user&password=$pass"

Now show my badges:

doReq /
for ((i=${#htbody[@]};i--;)) ;do
    line="${htbody[i]}"
    case $line in
        *badge1* ) line="${htbody[i-1]}${htbody[i]}${htbody[i+1]}"
                   line=${line//>+([0-9])</><} line=${line//<*([^>])>}
                   printf '%b\n' "${line//&#9679;/ \\U25cf }" ;;
esac ; done

On my desk, with my account, this prints:

 ? 13 gold badges ? 88 silver badges ? 112 bronze badges

(just now).

Of course, you're now ready to run doReq any time you like, as the connection stays open. We are now in environment/condition cited in the Introduction. (The version on my site does a forever loop, requesting this in a more efficient way, each rounded two minutes (EPOCHSECONDS % 120), up to user interaction. See at the bottom of this.)

...

Once you're done, before exit, you could stop openssl and close your file descriptor:

(I've added ls and ps as debug commands.)

ls -l /dev/fd/ ; ps --tty $(tty) ufw
kill $osslpid
exec {wwwE}<&-
exec {wwwI}>&-
exec {wwwO}<&-
ls -l /dev/fd/ ; ps --tty $(tty) ufw

This shows:

total 0
lrwx------ 1 user user 64 jui  2 13:52 0 -> /dev/pts/2
l-wx------ 1 user user 64 jui  2 13:52 1 -> /dev/pts/2
lrwx------ 1 user user 64 jui  2 13:52 10 -> pipe:[940266653]
lrwx------ 1 user user 64 jui  2 13:52 11 -> pipe:[940266654]
lr-x------ 1 user user 64 jui  2 13:52 12 -> pipe:[940266655]
lrwx------ 1 user user 64 jui  2 13:52 2 -> /dev/pts/2
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
user    28110  0.0  0.0  11812  7144 pts/7    Ss   jui01   0:02 bash
user    14038 30.0  0.0   9116  4228 pts/7    S+   13:52   0:00  \_ /bin/bash ./getSo.sh
user    14045  0.5  0.0   9356  5856 pts/7    S+   13:52   0:00       \_ openssl s_client -quiet -connect stackoverflow.com:443
user    14048  0.0  0.0  12404  3400 pts/7    R+   13:52   0:00       \_ ps --tty /dev/pts/7 ufw

total 0
lrwx------ 1 user user 64 jui  2 13:52 0 -> /dev/pts/2
l-wx------ 1 user user 64 jui  2 13:52 1 -> /dev/pts/2
lrwx------ 1 user user 64 jui  2 13:52 2 -> /dev/pts/2
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
user    28110  0.0  0.0  11812  7144 pts/7    Ss   jui01   0:02 bash
user    14038 30.0  0.0   9632  4756 pts/7    S+   13:52   0:00  \_ /bin/bash ./getSo.sh
user    14051  0.0  0.0  12404  3332 pts/7    R+   13:52   0:00      \_ ps --tty /dev/pts/7 ufw

The OpenSSL process is done and all three file descriptors become closed.

You could find this script (less condensed), with an extended main loop at getSo.sh.txt, getSo.sh.

Solution 2:[2]

It fully depends on the target website, but for example for Stack Overflow you can use the commands below:

curl -X POST "https://stackoverflow.com/users/login" -H "Content-Type: application/x-www-form-urlencoded" -d "[email protected]" -d "password=password" -c cookie.txt

# Verify your login
curl -b cookie.txt https://stackoverflow.com | grep YOUR_NAME
# OR check the status code from edit profile that should be 200 OK
curl -b cookie.txt https://stackoverflow.com/users/edit/YOUR_USER_ID -i 2>/dev/null

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Peter Mortensen
Solution 2 Peter Mortensen